can use MediaWikiTitleCodec::getTitleInvalidRegex() instead.
* HTMLForm & VFormHTMLForm::isVForm(), deprecated in 1.25, have been removed.
* The ProfileSection class, deprecated in 1.25 and unused, has been removed.
+* Wikimedia\Rdbms\SavepointPostgres is deprecated.
== Compatibility ==
MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported,
Currently only data attributes reserved to MediaWiki are allowed
(see Sanitizer::isReservedDataAttribute).
+'DeleteUnknownPreferences': Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which
+to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences
+that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed
+with 'gadget-', and so anything with that prefix is excluded from the deletion.
+&where: An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted
+ from the user_properties table.
+$db: The IDatabase object, useful for accessing $db->buildLike() etc.
+
'DifferenceEngineAfterLoadNewText': called in DifferenceEngine::loadNewText()
after the new revision's content has been loaded into the class member variable
$differenceEngine->mNewContent but before returning true from this function.
"apihelp-query+exturlusage-param-namespace": "Die aufzulistenden Seiten-Namensräume.",
"apihelp-query+exturlusage-param-limit": "Wie viele Seiten zurückgegeben werden sollen.",
"apihelp-query+exturlusage-param-expandurl": "Expandiert protokollrelative URLs mit dem kanonischen Protokoll.",
- "apihelp-query+exturlusage-example-simple": "Zeigt Seiten, die auf <kbd>http://www.mediawiki.org</kbd> verlinken.",
+ "apihelp-query+exturlusage-example-simple": "Zeigt Seiten, die auf <kbd>https://www.mediawiki.org</kbd> verlinken.",
"apihelp-query+filearchive-summary": "Alle gelöschten Dateien der Reihe nach auflisten.",
"apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
"apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
"apihelp-query+exturlusage-param-namespace": "Los espacios de nombres que enumerar.",
"apihelp-query+exturlusage-param-limit": "Cuántas páginas se devolverán.",
"apihelp-query+exturlusage-param-expandurl": "Expandir las URL relativas a un protocolo con el protocolo canónico.",
- "apihelp-query+exturlusage-example-simple": "Mostrar páginas que enlacen con <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Mostrar páginas que enlacen con <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Enumerar todos los archivos borrados de forma secuencial.",
"apihelp-query+filearchive-param-from": "El título de imagen para comenzar la enumeración",
"apihelp-query+filearchive-param-to": "El título de imagen para detener la enumeración.",
"apihelp-query+exturlusage-param-namespace": "Les espaces de nom à énumérer.",
"apihelp-query+exturlusage-param-limit": "Combien de pages renvoyer.",
"apihelp-query+exturlusage-param-expandurl": "Étendre les URLs relatives au protocole avec le protocole canonique.",
- "apihelp-query+exturlusage-example-simple": "Afficher les pages avec un lien vers <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Afficher les pages avec un lien vers <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Énumérer séquentiellement tous les fichiers supprimés.",
"apihelp-query+filearchive-param-from": "Le titre de l’image auquel démarrer l’énumération.",
"apihelp-query+filearchive-param-to": "Le titre de l’image auquel arrêter l’énumération.",
"apihelp-query+exturlusage-param-namespace": "Espazo de nomes a enumerar.",
"apihelp-query+exturlusage-param-limit": "Cantas páxinas devolver.",
"apihelp-query+exturlusage-param-expandurl": "Expandir as URLs relativas a un protocolo co protocolo canónico.",
- "apihelp-query+exturlusage-example-simple": "Mostrar páxinas ligando a <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Mostrar páxinas ligando a <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Enumerar secuencialmente todos os ficheiros borrados.",
"apihelp-query+filearchive-param-from": "Título da imaxe coa que comezar a enumeración.",
"apihelp-query+filearchive-param-to": "Título da imaxe coa que rematar a enumeración.",
"apihelp-query+exturlusage-param-namespace": "איזה מרחב שם למנות.",
"apihelp-query+exturlusage-param-limit": "כמה דפים להחזיר.",
"apihelp-query+exturlusage-param-expandurl": "הרחבת URL־ים בעלי פרוטוקול יחסי בפרוטוקול קנוני.",
- "apihelp-query+exturlusage-example-simple": "הצגת דפים שמקשרים ל־<kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "הצגת דפים שמקשרים ל־<kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "למנות את כל הקבצים המחוקים לפי הסדר.",
"apihelp-query+filearchive-param-from": "מאיזו כותרת תמונה להתחיל למנות.",
"apihelp-query+filearchive-param-to": "באיזו כותרת תמונה להפסיק למנות.",
"apihelp-query+recentchanges-paramvalue-prop-sizes": "הוספת אורך הדף החדש והישן בבייטים.",
"apihelp-query+recentchanges-paramvalue-prop-redirect": "מתייג שהדף הוא הפניה.",
"apihelp-query+recentchanges-paramvalue-prop-patrolled": "מתייג עריכה בת־בדיקה בתור בדוקה או בלתי־בדוקה.",
+ "apihelp-query+recentchanges-paramvalue-prop-autopatrolled": "ציון האם עריכות הניתנות לבדיקה נבדקו אוטומטית או לא.",
"apihelp-query+recentchanges-paramvalue-prop-loginfo": "הוספת מידע יומן (זהה יומן, סוג יומן וכו') לעיולי יומן.",
"apihelp-query+recentchanges-paramvalue-prop-tags": "רשימת תגים עבור העיול.",
"apihelp-query+recentchanges-paramvalue-prop-sha1": "הוספת סיכום־ביקורת תוכן לעיולים שמשויכים לגרסה.",
"apihelp-query+usercontribs-paramvalue-prop-sizediff": "הוספת ההפרש של העריכה אל מול ההורה שלה.",
"apihelp-query+usercontribs-paramvalue-prop-flags": "הוספת הדגלים של העריכה.",
"apihelp-query+usercontribs-paramvalue-prop-patrolled": "מתייג עריכות בדוקות.",
+ "apihelp-query+usercontribs-paramvalue-prop-autopatrolled": "תיוג עריכות שנבדקו אוטומטית.",
"apihelp-query+usercontribs-paramvalue-prop-tags": "רשימת תגים עבור עריכות.",
"apihelp-query+usercontribs-param-show": "הצגה רק של פריטים שמתאימים לאמות המידה האלה, למשל רק עריכות לא־משניות.\n\nאם מוגדר <kbd>$2show=patrolled</kbd> או <kbd>$2show=!patrolled</kbd>, גרסאות ישנות מ־<var dir=\"ltr\">[[mw:Special:MyLanguage/Manual:$wgRCMaxAge|$wgRCMaxAge]]</var> ({{PLURAL:$1|שנייה אחת|$1 שניות}}) לא תוצגנה.",
"apihelp-query+usercontribs-param-tag": "לרשום רק גרסאות עם התג הזה.",
"apihelp-query+exturlusage-param-protocol": "Az URL protokollja. Ha üres és az <var>$1query</var> paraméter meg van adva, a protokoll <kbd>http</kbd>. Hagyd ezt és az <var>$1query</var> paramétert is üresen az összes külső link listázásához.",
"apihelp-query+exturlusage-param-namespace": "A listázandó névtér.",
"apihelp-query+exturlusage-param-limit": "A visszaadandó lapok száma.",
- "apihelp-query+exturlusage-example-simple": "A <kbd>http://www.mediawiki.org</kbd> URL-re hivatkozó lapok megjelenítése.",
+ "apihelp-query+exturlusage-example-simple": "A <kbd>https://www.mediawiki.org</kbd> URL-re hivatkozó lapok megjelenítése.",
"apihelp-query+filearchive-summary": "Az összes törölt fájl visszaadása.",
"apihelp-query+filearchive-param-from": "A fájlok listázása ettől a címtől.",
"apihelp-query+filearchive-param-to": "A fájlok listázása eddig a címig.",
"apihelp-query+exturlusage-param-query": "プロトコルを除いた検索文字列。[[Special:LinkSearch]] も参照してください。すべての外部リンクを一覧表示するには空欄にしてください。",
"apihelp-query+exturlusage-param-namespace": "列挙するページ名前空間。",
"apihelp-query+exturlusage-param-limit": "返すページの数。",
- "apihelp-query+exturlusage-example-simple": "<kbd>http://www.mediawiki.org</kbd> にリンクしているページを一覧表示する。",
+ "apihelp-query+exturlusage-example-simple": "<kbd>https://www.mediawiki.org</kbd> にリンクしているページを一覧表示する。",
"apihelp-query+filearchive-summary": "削除されたファイルをすべて順に列挙します。",
"apihelp-query+filearchive-param-from": "列挙の始点となる画像のページ名。",
"apihelp-query+filearchive-param-to": "列挙の終点となる画像のページ名。",
"apihelp-query+exturlusage-paramvalue-prop-ids": "Prideda puslapio ID.",
"apihelp-query+exturlusage-paramvalue-prop-url": "Prideda URL, panaudota puslapyje.",
"apihelp-query+exturlusage-param-limit": "Kiek puslapių gražinti.",
- "apihelp-query+exturlusage-example-simple": "Rodyti puslapius, nurodančius į <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Rodyti puslapius, nurodančius į <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-param-prop": "Kokią paveikslėlio informaciją gauti:",
"apihelp-query+filearchive-paramvalue-prop-timestamp": "Prideda laiko žymę įkeltai versijai.",
"apihelp-query+filearchive-paramvalue-prop-user": "Prideda vartotoją, kuris įkėlė paveikslėlio versiją.",
"apihelp-query+exturlusage-param-namespace": "O espaço nominal das páginas para enumerar.",
"apihelp-query+exturlusage-param-limit": "Quantas páginas retornar.",
"apihelp-query+exturlusage-param-expandurl": "Expandir URLs relativos ao protocolo com o protocolo canônico.",
- "apihelp-query+exturlusage-example-simple": "Mostra páginas vigiadas à <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Mostra páginas vigiadas à <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Enumerar todos os arquivos excluídos sequencialmente.",
"apihelp-query+filearchive-param-from": "O título da imagem do qual começar a enumeração.",
"apihelp-query+filearchive-param-to": "O título da imagem no qual parar a enumeração.",
"apihelp-query+exturlusage-param-namespace": "Os espaços nominais a serem enumerados.",
"apihelp-query+exturlusage-param-limit": "O número de páginas a serem devolvidas.",
"apihelp-query+exturlusage-param-expandurl": "Expandir os URL relativos a protocolo com o protocolo canónico.",
- "apihelp-query+exturlusage-example-simple": "Mostrar as páginas com hiperligações para <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Mostrar as páginas com hiperligações para <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Enumerar todos os ficheiros eliminados sequencialmente.",
"apihelp-query+filearchive-param-from": "O título da imagem a partir do qual será começada a enumeração.",
"apihelp-query+filearchive-param-to": "O título da imagem no qual será terminada a enumeração.",
"apihelp-query+exturlusage-param-namespace": "Пространства имён для перечисления.",
"apihelp-query+exturlusage-param-limit": "Сколько страниц вернуть.",
"apihelp-query+exturlusage-param-expandurl": "Раскрыть зависимые от протокола ссылки с какноничным протоколом.",
- "apihelp-query+exturlusage-example-simple": "Показать страницы, ссылающиеся на <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Показать страницы, ссылающиеся на <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Перечисление всех удалённых файлов.",
"apihelp-query+filearchive-param-from": "Название изображения, с которого начать перечисление.",
"apihelp-query+filearchive-param-to": "Название изображения, на котором закончить перечисление.",
"apihelp-query+exturlusage-param-namespace": "Простори назв для переліку.",
"apihelp-query+exturlusage-param-limit": "Скільки сторінок виводити.",
"apihelp-query+exturlusage-param-expandurl": "Розгорнути протокол-залежні URL за канонічним протоколом.",
- "apihelp-query+exturlusage-example-simple": "Показати сторінки, які посилаються на <kbd>http://www.mediawiki.org</kbd>.",
+ "apihelp-query+exturlusage-example-simple": "Показати сторінки, які посилаються на <kbd>https://www.mediawiki.org</kbd>.",
"apihelp-query+filearchive-summary": "Перерахувати всі вилучені файли послідовно.",
"apihelp-query+filearchive-param-from": "Назва зображення, з якої почати перелічувати.",
"apihelp-query+filearchive-param-to": "Назва зображення, якою закінчити перелічувати.",
"apihelp-query+exturlusage-param-namespace": "要列举的页面名字空间。",
"apihelp-query+exturlusage-param-limit": "返回多少页面。",
"apihelp-query+exturlusage-param-expandurl": "用标准协议展开协议相关URL。",
- "apihelp-query+exturlusage-example-simple": "显示链接至<kbd>http://www.mediawiki.org</kbd>的页面。",
+ "apihelp-query+exturlusage-example-simple": "显示链接至<kbd>https://www.mediawiki.org</kbd>的页面。",
"apihelp-query+filearchive-summary": "循序列举所有被删除的文件。",
"apihelp-query+filearchive-param-from": "枚举的起始图片标题。",
"apihelp-query+filearchive-param-to": "枚举的结束图片标题。",
* @ingroup Database
*/
abstract class MWLBFactory {
+
+ /** @var array Cache of already-logged deprecation messages */
+ private static $loggedDeprecations = [];
+
/**
* @param array $lbConf Config for LBFactory::__construct()
* @param Config $mainConfig Main config object from MediaWikiServices
'connLogger' => LoggerFactory::getInstance( 'DBConnection' ),
'perfLogger' => LoggerFactory::getInstance( 'DBPerformance' ),
'errorLogger' => [ MWExceptionHandler::class, 'logException' ],
+ 'deprecationLogger' => [ static::class, 'logDeprecation' ],
'cliMode' => $wgCommandLineMode,
'hostname' => wfHostname(),
'readOnlyReason' => $readOnlyMode->getReason(),
] );
}
}
+
+ /**
+ * Log a database deprecation warning
+ * @param string $msg Deprecation message
+ */
+ public static function logDeprecation( $msg ) {
+ global $wgDevelopmentWarnings;
+
+ if ( isset( self::$loggedDeprecations[$msg] ) ) {
+ return;
+ }
+ self::$loggedDeprecations[$msg] = true;
+
+ if ( $wgDevelopmentWarnings ) {
+ trigger_error( $msg, E_USER_DEPRECATED );
+ }
+ wfDebugLog( 'deprecated', $msg, 'private' );
+ }
}
protected $queryLogger;
/** @var callback Error logging callback */
protected $errorLogger;
+ /** @var callback Deprecation logging callback */
+ protected $deprecationLogger;
/** @var resource|null Database connection */
protected $conn = null;
$this->connLogger = $params['connLogger'];
$this->queryLogger = $params['queryLogger'];
$this->errorLogger = $params['errorLogger'];
+ $this->deprecationLogger = $params['deprecationLogger'];
if ( isset( $params['nonNativeInsertSelectBatchSize'] ) ) {
$this->nonNativeInsertSelectBatchSize = $params['nonNativeInsertSelectBatchSize'];
* includes the agent as a SQL comment.
* - trxProfiler: Optional TransactionProfiler instance.
* - errorLogger: Optional callback that takes an Exception and logs it.
+ * - deprecationLogger: Optional callback that takes a string and logs it.
* - cliMode: Whether to consider the execution context that of a CLI script.
* - agent: Optional name used to identify the end-user in query profiling/logging.
* - srvCache: Optional BagOStuff instance to an APC-style cache.
trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
};
}
+ if ( !isset( $p['deprecationLogger'] ) ) {
+ $p['deprecationLogger'] = function ( $msg ) {
+ trigger_error( $msg, E_USER_DEPRECATED );
+ };
+ }
/** @var Database $conn */
$conn = new $class( $p );
# In the first case, the only options going forward are (a) ROLLBACK, or
# (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
# option is ROLLBACK, since the snapshots would have been released.
- if ( is_object( $tempIgnore ) ) {
- // Ugly hack to know that savepoints are in use for postgres
- // FIXME: remove this and make DatabasePostgres use ATOMIC_CANCELABLE
- } else {
- $this->trxStatus = self::STATUS_TRX_ERROR;
- $this->trxStatusCause =
- $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
- $tempIgnore = false; // cannot recover
- }
+ $this->trxStatus = self::STATUS_TRX_ERROR;
+ $this->trxStatusCause =
+ $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
+ $tempIgnore = false; // cannot recover
} else {
- # Nothing prior was there to lose from the transaction
+ # Nothing prior was there to lose from the transaction,
+ # so just roll it back.
+ $this->doRollback( __METHOD__ . " ($fname)" );
$this->trxStatus = self::STATUS_TRX_OK;
}
}
private function handleSessionLoss() {
// Clean up tracking of session-level things...
// https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html
- // https://www.postgresql.org/docs/9.1/static/sql-createtable.html (ignoring ON COMMIT)
+ // https://www.postgresql.org/docs/9.2/static/sql-createtable.html (ignoring ON COMMIT)
$this->sessionTempTables = [];
// https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
// https://www.postgresql.org/docs/9.4/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
/** @var resource */
protected $lastResultHandle = null;
- /** @var int The number of rows affected as an integer */
- protected $lastAffectedRowCount = null;
/** @var float|string */
private $numericVersion = null;
private $connectString;
/** @var string */
private $coreSchema;
+ /** @var string */
+ private $tempSchema;
/** @var string[] Map of (reserved table name => alternate table name) */
private $keywordTableMap = [];
}
public function hasConstraint( $name ) {
- $conn = $this->getBindingHandle();
-
- $sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " .
- "WHERE c.connamespace = n.oid AND conname = '" .
- pg_escape_string( $conn, $name ) . "' AND n.nspname = '" .
- pg_escape_string( $conn, $this->getCoreSchema() ) . "'";
- $res = $this->doQuery( $sql );
-
- return $this->numRows( $res );
+ foreach ( $this->getCoreSchemas() as $schema ) {
+ $sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " .
+ "WHERE c.connamespace = n.oid AND conname = " .
+ $this->addQuotes( $name ) . " AND n.nspname = " .
+ $this->addQuotes( $schema );
+ $res = $this->doQuery( $sql );
+ if ( $res && $this->numRows( $res ) ) {
+ return true;
+ }
+ }
+ return false;
}
public function open( $server, $user, $password, $dbName ) {
$this->query( "SET datestyle = 'ISO, YMD'", __METHOD__ );
$this->query( "SET timezone = 'GMT'", __METHOD__ );
$this->query( "SET standard_conforming_strings = on", __METHOD__ );
- if ( $this->getServerVersion() >= 9.0 ) {
- $this->query( "SET bytea_output = 'escape'", __METHOD__ ); // PHP bug 53127
- }
+ $this->query( "SET bytea_output = 'escape'", __METHOD__ ); // PHP bug 53127
$this->determineCoreSchema( $this->schema );
// The schema to be used is now in the search path; no need for explicit qualification
throw new DBUnexpectedError( $this, "Unable to post new query to PostgreSQL\n" );
}
$this->lastResultHandle = pg_get_result( $conn );
- $this->lastAffectedRowCount = null;
if ( pg_result_error( $this->lastResultHandle ) ) {
return false;
}
}
protected function fetchAffectedRowCount() {
- if ( !is_null( $this->lastAffectedRowCount ) ) {
- // Forced result for simulated queries
- return $this->lastAffectedRowCount;
- }
if ( !$this->lastResultHandle ) {
return 0;
}
public function indexAttributes( $index, $schema = false ) {
if ( $schema === false ) {
- $schema = $this->getCoreSchema();
- }
- /*
- * A subquery would be not needed if we didn't care about the order
- * of attributes, but we do
- */
- $sql = <<<__INDEXATTR__
-
- SELECT opcname,
- attname,
- i.indoption[s.g] as option,
- pg_am.amname
- FROM
- (SELECT generate_series(array_lower(isub.indkey,1), array_upper(isub.indkey,1)) AS g
- FROM
- pg_index isub
- JOIN pg_class cis
- ON cis.oid=isub.indexrelid
- JOIN pg_namespace ns
- ON cis.relnamespace = ns.oid
- WHERE cis.relname='$index' AND ns.nspname='$schema') AS s,
- pg_attribute,
- pg_opclass opcls,
- pg_am,
- pg_class ci
- JOIN pg_index i
- ON ci.oid=i.indexrelid
- JOIN pg_class ct
- ON ct.oid = i.indrelid
- JOIN pg_namespace n
- ON ci.relnamespace = n.oid
- WHERE
- ci.relname='$index' AND n.nspname='$schema'
- AND attrelid = ct.oid
- AND i.indkey[s.g] = attnum
- AND i.indclass[s.g] = opcls.oid
- AND pg_am.oid = opcls.opcmethod
+ $schemas = $this->getCoreSchemas();
+ } else {
+ $schemas = [ $schema ];
+ }
+
+ $eindex = $this->addQuotes( $index );
+
+ foreach ( $schemas as $schema ) {
+ $eschema = $this->addQuotes( $schema );
+ /*
+ * A subquery would be not needed if we didn't care about the order
+ * of attributes, but we do
+ */
+ $sql = <<<__INDEXATTR__
+
+ SELECT opcname,
+ attname,
+ i.indoption[s.g] as option,
+ pg_am.amname
+ FROM
+ (SELECT generate_series(array_lower(isub.indkey,1), array_upper(isub.indkey,1)) AS g
+ FROM
+ pg_index isub
+ JOIN pg_class cis
+ ON cis.oid=isub.indexrelid
+ JOIN pg_namespace ns
+ ON cis.relnamespace = ns.oid
+ WHERE cis.relname=$eindex AND ns.nspname=$eschema) AS s,
+ pg_attribute,
+ pg_opclass opcls,
+ pg_am,
+ pg_class ci
+ JOIN pg_index i
+ ON ci.oid=i.indexrelid
+ JOIN pg_class ct
+ ON ct.oid = i.indrelid
+ JOIN pg_namespace n
+ ON ci.relnamespace = n.oid
+ WHERE
+ ci.relname=$eindex AND n.nspname=$eschema
+ AND attrelid = ct.oid
+ AND i.indkey[s.g] = attnum
+ AND i.indclass[s.g] = opcls.oid
+ AND pg_am.oid = opcls.opcmethod
__INDEXATTR__;
- $res = $this->query( $sql, __METHOD__ );
- $a = [];
- if ( $res ) {
- foreach ( $res as $row ) {
- $a[] = [
- $row->attname,
- $row->opcname,
- $row->amname,
- $row->option ];
+ $res = $this->query( $sql, __METHOD__ );
+ $a = [];
+ if ( $res ) {
+ foreach ( $res as $row ) {
+ $a[] = [
+ $row->attname,
+ $row->opcname,
+ $row->amname,
+ $row->option ];
+ }
+ return $a;
}
- } else {
- return null;
}
-
- return $a;
+ return null;
}
public function indexUnique( $table, $index, $fname = __METHOD__ ) {
return parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
}
- /**
- * INSERT wrapper, inserts an array into a table
- *
- * $args may be a single associative array, or an array of these with numeric keys,
- * for multi-row insert (Postgres version 8.2 and above only).
- *
- * @param string $table Name of the table to insert to.
- * @param array $args Items to insert into the table.
- * @param string $fname Name of the function, for profiling
- * @param array|string $options String or array. Valid options: IGNORE
- * @return bool Success of insert operation. IGNORE always returns true.
- */
+ /** @inheritDoc */
public function insert( $table, $args, $fname = __METHOD__, $options = [] ) {
if ( !count( $args ) ) {
return true;
}
if ( isset( $args[0] ) && is_array( $args[0] ) ) {
- $multi = true;
+ $rows = $args;
$keys = array_keys( $args[0] );
} else {
- $multi = false;
+ $rows = [ $args ];
$keys = array_keys( $args );
}
- // If IGNORE is set, we use savepoints to emulate mysql's behavior
- // @todo If PostgreSQL 9.5+, we could use ON CONFLICT DO NOTHING instead
- $savepoint = $olde = null;
- $numrowsinserted = 0;
- if ( in_array( 'IGNORE', $options ) ) {
- $savepoint = new SavepointPostgres( $this, 'mw', $this->queryLogger );
- $olde = error_reporting( 0 );
- // For future use, we may want to track the number of actual inserts
- // Right now, insert (all writes) simply return true/false
- }
+ $ignore = in_array( 'IGNORE', $options );
$sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
- if ( $multi ) {
- if ( $this->numericVersion >= 8.2 && !$savepoint ) {
- $first = true;
- foreach ( $args as $row ) {
- if ( $first ) {
- $first = false;
- } else {
- $sql .= ',';
- }
- $sql .= '(' . $this->makeList( $row ) . ')';
+ if ( $this->numericVersion >= 9.5 || !$ignore ) {
+ // No IGNORE or our PG has "ON CONFLICT DO NOTHING"
+ $first = true;
+ foreach ( $rows as $row ) {
+ if ( $first ) {
+ $first = false;
+ } else {
+ $sql .= ',';
}
- $res = (bool)$this->query( $sql, $fname, $savepoint );
- } else {
- $res = true;
- $origsql = $sql;
- foreach ( $args as $row ) {
- $tempsql = $origsql;
+ $sql .= '(' . $this->makeList( $row ) . ')';
+ }
+ if ( $ignore ) {
+ $sql .= ' ON CONFLICT DO NOTHING';
+ }
+ $this->query( $sql, $fname );
+ } else {
+ // Emulate IGNORE by doing each row individually, with savepoints
+ // to roll back as necessary.
+ $numrowsinserted = 0;
+
+ $tok = $this->startAtomic( "$fname (outer)", self::ATOMIC_CANCELABLE );
+ try {
+ foreach ( $rows as $row ) {
+ $tempsql = $sql;
$tempsql .= '(' . $this->makeList( $row ) . ')';
- if ( $savepoint ) {
- $savepoint->savepoint();
- }
-
- $tempres = (bool)$this->query( $tempsql, $fname, $savepoint );
-
- if ( $savepoint ) {
- $bar = pg_result_error( $this->lastResultHandle );
- if ( $bar != false ) {
- $savepoint->rollback();
- } else {
- $savepoint->release();
- $numrowsinserted++;
+ $this->startAtomic( "$fname (inner)", self::ATOMIC_CANCELABLE );
+ try {
+ $this->query( $tempsql, $fname );
+ $this->endAtomic( "$fname (inner)" );
+ $numrowsinserted++;
+ } catch ( DBQueryError $e ) {
+ $this->cancelAtomic( "$fname (inner)" );
+ // Our IGNORE is supposed to ignore duplicate key errors, but not others.
+ // (even though MySQL's version apparently ignores all errors)
+ if ( $e->errno !== '23505' ) {
+ throw $e;
}
}
-
- // If any of them fail, we fail overall for this function call
- // Note that this will be ignored if IGNORE is set
- if ( !$tempres ) {
- $res = false;
- }
- }
- }
- } else {
- // Not multi, just a lone insert
- if ( $savepoint ) {
- $savepoint->savepoint();
- }
-
- $sql .= '(' . $this->makeList( $args ) . ')';
- $res = (bool)$this->query( $sql, $fname, $savepoint );
- if ( $savepoint ) {
- $bar = pg_result_error( $this->lastResultHandle );
- if ( $bar != false ) {
- $savepoint->rollback();
- } else {
- $savepoint->release();
- $numrowsinserted++;
}
+ } catch ( Exception $e ) {
+ $this->cancelAtomic( "$fname (outer)", $tok );
+ throw $e;
}
- }
- if ( $savepoint ) {
- error_reporting( $olde );
- $savepoint->commit();
+ $this->endAtomic( "$fname (outer)" );
// Set the affected row count for the whole operation
- $this->lastAffectedRowCount = $numrowsinserted;
-
- // IGNORE always returns true
- return true;
+ $this->affectedRowCount = $numrowsinserted;
}
- return $res;
+ return true;
}
/**
$insertOptions = [ $insertOptions ];
}
- /*
- * If IGNORE is set, use the non-native version.
- * @todo If PostgreSQL 9.5+, we could use ON CONFLICT DO NOTHING
- */
if ( in_array( 'IGNORE', $insertOptions ) ) {
- return $this->nonNativeInsertSelect(
- $destTable, $srcTable, $varMap, $conds, $fname, $insertOptions, $selectOptions, $selectJoinConds
- );
+ if ( $this->getServerVersion() >= 9.5 ) {
+ // Use ON CONFLICT DO NOTHING if we have it for IGNORE
+ $destTable = $this->tableName( $destTable );
+
+ $selectSql = $this->selectSQLText(
+ $srcTable,
+ array_values( $varMap ),
+ $conds,
+ $fname,
+ $selectOptions,
+ $selectJoinConds
+ );
+
+ $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ') ' .
+ $selectSql . ' ON CONFLICT DO NOTHING';
+
+ return $this->query( $sql, $fname );
+ } else {
+ // IGNORE and we don't have ON CONFLICT DO NOTHING, so just use the non-native version
+ return $this->nonNativeInsertSelect(
+ $destTable, $srcTable, $varMap, $conds, $fname,
+ $insertOptions, $selectOptions, $selectJoinConds
+ );
+ }
}
return parent::nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname,
}
public function wasDeadlock() {
- // https://www.postgresql.org/docs/8.2/static/errcodes-appendix.html
+ // https://www.postgresql.org/docs/9.2/static/errcodes-appendix.html
return $this->lastErrno() === '40P01';
}
public function wasLockTimeout() {
- // https://www.postgresql.org/docs/8.2/static/errcodes-appendix.html
+ // https://www.postgresql.org/docs/9.2/static/errcodes-appendix.html
return $this->lastErrno() === '55P03';
}
public function wasConnectionError( $errno ) {
- // https://www.postgresql.org/docs/8.2/static/errcodes-appendix.html
+ // https://www.postgresql.org/docs/9.2/static/errcodes-appendix.html
static $codes = [ '08000', '08003', '08006', '08001', '08004', '57P01', '57P03', '53300' ];
return in_array( $errno, $codes, true );
public function duplicateTableStructure(
$oldName, $newName, $temporary = false, $fname = __METHOD__
) {
- $newName = $this->addIdentifierQuotes( $newName );
- $oldName = $this->addIdentifierQuotes( $oldName );
+ $newNameE = $this->addIdentifierQuotes( $newName );
+ $oldNameE = $this->addIdentifierQuotes( $oldName );
+
+ $ret = $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newNameE " .
+ "(LIKE $oldNameE INCLUDING DEFAULTS INCLUDING INDEXES)", $fname );
+ if ( !$ret ) {
+ return $ret;
+ }
+
+ $res = $this->query( 'SELECT attname FROM pg_class c'
+ . ' JOIN pg_namespace n ON (n.oid = c.relnamespace)'
+ . ' JOIN pg_attribute a ON (a.attrelid = c.oid)'
+ . ' JOIN pg_attrdef d ON (c.oid=d.adrelid and a.attnum=d.adnum)'
+ . ' WHERE relkind = \'r\''
+ . ' AND nspname = ' . $this->addQuotes( $this->getCoreSchema() )
+ . ' AND relname = ' . $this->addQuotes( $oldName )
+ . ' AND adsrc LIKE \'nextval(%\'',
+ $fname
+ );
+ $row = $this->fetchObject( $res );
+ if ( $row ) {
+ $field = $row->attname;
+ $newSeq = "{$newName}_{$field}_seq";
+ $fieldE = $this->addIdentifierQuotes( $field );
+ $newSeqE = $this->addIdentifierQuotes( $newSeq );
+ $newSeqQ = $this->addQuotes( $newSeq );
+ $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " SEQUENCE $newSeqE", $fname );
+ $this->query(
+ "ALTER TABLE $newNameE ALTER COLUMN $fieldE SET DEFAULT nextval({$newSeqQ}::regclass)",
+ $fname
+ );
+ }
- return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName " .
- "(LIKE $oldName INCLUDING DEFAULTS INCLUDING INDEXES)", $fname );
+ return $ret;
+ }
+
+ public function resetSequenceForTable( $table, $fname = __METHOD__ ) {
+ $table = $this->tableName( $table, 'raw' );
+ foreach ( $this->getCoreSchemas() as $schema ) {
+ $res = $this->query(
+ 'SELECT c.oid FROM pg_class c JOIN pg_namespace n ON (n.oid = c.relnamespace)'
+ . ' WHERE relkind = \'r\''
+ . ' AND nspname = ' . $this->addQuotes( $schema )
+ . ' AND relname = ' . $this->addQuotes( $table ),
+ $fname
+ );
+ if ( !$res || !$this->numRows( $res ) ) {
+ continue;
+ }
+
+ $oid = $this->fetchObject( $res )->oid;
+ $res = $this->query( 'SELECT adsrc FROM pg_attribute a'
+ . ' JOIN pg_attrdef d ON (a.attrelid=d.adrelid and a.attnum=d.adnum)'
+ . " WHERE a.attrelid = $oid"
+ . ' AND adsrc LIKE \'nextval(%\'',
+ $fname
+ );
+ $row = $this->fetchObject( $res );
+ if ( $row ) {
+ $this->query(
+ 'SELECT ' . preg_replace( '/^nextval\((.+)\)$/', 'setval($1,1,false)', $row->adsrc ),
+ $fname
+ );
+ return true;
+ }
+ return false;
+ }
+
+ return false;
}
public function listTables( $prefix = null, $fname = __METHOD__ ) {
- $eschema = $this->addQuotes( $this->getCoreSchema() );
+ $eschemas = implode( ',', array_map( [ $this, 'addQuotes' ], $this->getCoreSchemas() ) );
$result = $this->query(
- "SELECT tablename FROM pg_tables WHERE schemaname = $eschema", $fname );
+ "SELECT DISTINCT tablename FROM pg_tables WHERE schemaname IN ($eschemas)", $fname );
$endArray = [];
foreach ( $result as $table ) {
return $this->coreSchema;
}
+ /**
+ * Return schema names for temporary tables and core application tables
+ *
+ * @since 1.31
+ * @return string[] schema names
+ */
+ public function getCoreSchemas() {
+ if ( $this->tempSchema ) {
+ return [ $this->tempSchema, $this->getCoreSchema() ];
+ }
+
+ $res = $this->query(
+ "SELECT nspname FROM pg_catalog.pg_namespace n WHERE n.oid = pg_my_temp_schema()", __METHOD__
+ );
+ $row = $this->fetchObject( $res );
+ if ( $row ) {
+ $this->tempSchema = $row->nspname;
+ return [ $this->tempSchema, $this->getCoreSchema() ];
+ }
+
+ return [ $this->getCoreSchema() ];
+ }
+
public function getServerVersion() {
if ( !isset( $this->numericVersion ) ) {
$conn = $this->getBindingHandle();
$types = [ $types ];
}
if ( $schema === false ) {
- $schema = $this->getCoreSchema();
+ $schemas = $this->getCoreSchemas();
+ } else {
+ $schemas = [ $schema ];
}
$table = $this->realTableName( $table, 'raw' );
$etable = $this->addQuotes( $table );
- $eschema = $this->addQuotes( $schema );
- $sql = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
- . "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
- . "AND c.relkind IN ('" . implode( "','", $types ) . "')";
- $res = $this->query( $sql );
- $count = $res ? $res->numRows() : 0;
+ foreach ( $schemas as $schema ) {
+ $eschema = $this->addQuotes( $schema );
+ $sql = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
+ . "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
+ . "AND c.relkind IN ('" . implode( "','", $types ) . "')";
+ $res = $this->query( $sql );
+ if ( $res && $res->numRows() ) {
+ return true;
+ }
+ }
- return (bool)$count;
+ return false;
}
/**
AND tgrelid=pg_class.oid
AND nspname=%s AND relname=%s AND tgname=%s
SQL;
- $res = $this->query(
- sprintf(
- $q,
- $this->addQuotes( $this->getCoreSchema() ),
- $this->addQuotes( $table ),
- $this->addQuotes( $trigger )
- )
- );
- if ( !$res ) {
- return null;
+ foreach ( $this->getCoreSchemas() as $schema ) {
+ $res = $this->query(
+ sprintf(
+ $q,
+ $this->addQuotes( $schema ),
+ $this->addQuotes( $table ),
+ $this->addQuotes( $trigger )
+ )
+ );
+ if ( $res && $res->numRows() ) {
+ return true;
+ }
}
- $rows = $res->numRows();
- return $rows;
+ return false;
}
public function ruleExists( $table, $rule ) {
[
'rulename' => $rule,
'tablename' => $table,
- 'schemaname' => $this->getCoreSchema()
+ 'schemaname' => $this->getCoreSchemas()
]
);
}
public function constraintExists( $table, $constraint ) {
- $sql = sprintf( "SELECT 1 FROM information_schema.table_constraints " .
- "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
- $this->addQuotes( $this->getCoreSchema() ),
- $this->addQuotes( $table ),
- $this->addQuotes( $constraint )
- );
- $res = $this->query( $sql );
- if ( !$res ) {
- return null;
+ foreach ( $this->getCoreSchemas() as $schema ) {
+ $sql = sprintf( "SELECT 1 FROM information_schema.table_constraints " .
+ "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
+ $this->addQuotes( $schema ),
+ $this->addQuotes( $table ),
+ $this->addQuotes( $constraint )
+ );
+ $res = $this->query( $sql );
+ if ( $res && $res->numRows() ) {
+ return true;
+ }
}
- $rows = $res->numRows();
-
- return $rows;
+ return false;
}
/**
return "'" . pg_escape_string( $conn, (string)$s ) . "'";
}
- /**
- * Postgres specific version of replaceVars.
- * Calls the parent version in Database.php
- *
- * @param string $ins SQL string, read from a stream (usually tables.sql)
- * @return string SQL string
- */
- protected function replaceVars( $ins ) {
- $ins = parent::replaceVars( $ins );
-
- if ( $this->numericVersion >= 8.3 ) {
- // Thanks for not providing backwards-compatibility, 8.3
- $ins = preg_replace( "/to_tsvector\s*\(\s*'default'\s*,/", 'to_tsvector(', $ins );
- }
-
- if ( $this->numericVersion <= 8.1 ) { // Our minimum version
- $ins = str_replace( 'USING gin', 'USING gist', $ins );
- }
-
- return $ins;
- }
-
public function makeSelectOptions( $options ) {
$preLimitTail = $postLimitTail = '';
$startOpts = $useIndex = $ignoreIndex = '';
if ( !parent::lockIsFree( $lockName, $method ) ) {
return false; // already held
}
- // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+ // http://www.postgresql.org/docs/9.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
$key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
$result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key))
WHEN 'f' THEN 'f' ELSE pg_advisory_unlock($key) END) AS lockstatus", $method );
}
public function lock( $lockName, $method, $timeout = 5 ) {
- // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+ // http://www.postgresql.org/docs/9.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
$key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
$loop = new WaitConditionLoop(
function () use ( $lockName, $key, $timeout, $method ) {
}
public function unlock( $lockName, $method ) {
- // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+ // http://www.postgresql.org/docs/9.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
$key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
$result = $this->query( "SELECT pg_advisory_unlock($key) as lockstatus", $method );
$row = $this->fetchObject( $result );
* Manage savepoints within a transaction
* @ingroup Database
* @since 1.19
+ * @deprecated since 1.31, use IDatabase::startAtomic() and such instead.
*/
class SavepointPostgres {
/** @var DatabasePostgres Establish a savepoint within a transaction */
SQL;
$table = $db->remappedTableName( $table );
- $res = $db->query(
- sprintf( $q,
- $db->addQuotes( $db->getCoreSchema() ),
- $db->addQuotes( $table ),
- $db->addQuotes( $field )
- )
- );
- $row = $db->fetchObject( $res );
- if ( !$row ) {
- return null;
+ foreach ( $db->getCoreSchemas() as $schema ) {
+ $res = $db->query(
+ sprintf( $q,
+ $db->addQuotes( $schema ),
+ $db->addQuotes( $table ),
+ $db->addQuotes( $field )
+ )
+ );
+ $row = $db->fetchObject( $res );
+ if ( !$row ) {
+ continue;
+ }
+ $n = new PostgresField;
+ $n->type = $row->typname;
+ $n->nullable = ( $row->attnotnull == 'f' );
+ $n->name = $field;
+ $n->tablename = $table;
+ $n->max_length = $row->attlen;
+ $n->deferrable = ( $row->deferrable == 't' );
+ $n->deferred = ( $row->deferred == 't' );
+ $n->conname = $row->conname;
+ $n->has_default = ( $row->atthasdef === 't' );
+ $n->default = $row->adsrc;
+
+ return $n;
}
- $n = new PostgresField;
- $n->type = $row->typname;
- $n->nullable = ( $row->attnotnull == 'f' );
- $n->name = $field;
- $n->tablename = $table;
- $n->max_length = $row->attlen;
- $n->deferrable = ( $row->deferrable == 't' );
- $n->deferred = ( $row->deferred == 't' );
- $n->conname = $row->conname;
- $n->has_default = ( $row->atthasdef === 't' );
- $n->default = $row->adsrc;
- return $n;
+ return null;
}
function name() {
* - queryLogger: PSR-3 logger instance. [optional]
* - perfLogger: PSR-3 logger instance. [optional]
* - errorLogger: Callback that takes an Exception and logs it. [optional]
+ * - deprecationLogger: Callback to log a deprecation warning. [optional]
* @throws InvalidArgumentException
*/
public function __construct( array $conf );
protected $perfLogger;
/** @var callable Error logger */
protected $errorLogger;
+ /** @var callable Deprecation logger */
+ protected $deprecationLogger;
/** @var BagOStuff */
protected $srvCache;
/** @var BagOStuff */
$this->errorLogger = isset( $conf['errorLogger'] )
? $conf['errorLogger']
: function ( Exception $e ) {
- trigger_error( E_USER_WARNING, get_class( $e ) . ': ' . $e->getMessage() );
+ trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
+ };
+ $this->deprecationLogger = isset( $conf['deprecationLogger'] )
+ ? $conf['deprecationLogger']
+ : function ( $msg ) {
+ trigger_error( $msg, E_USER_DEPRECATED );
};
$this->profiler = isset( $conf['profiler'] ) ? $conf['profiler'] : null;
'connLogger' => $this->connLogger,
'replLogger' => $this->replLogger,
'errorLogger' => $this->errorLogger,
+ 'deprecationLogger' => $this->deprecationLogger,
'hostname' => $this->hostname,
'cliMode' => $this->cliMode,
'agent' => $this->agent,
* - queryLogger: PSR-3 logger instance. [optional]
* - perfLogger: PSR-3 logger instance. [optional]
* - errorLogger : Callback that takes an Exception and logs it. [optional]
+ * - deprecationLogger: Callback to log a deprecation warning. [optional]
* @throws InvalidArgumentException
*/
public function __construct( array $params );
/** @var callable Exception logger */
private $errorLogger;
+ /** @var callable Deprecation logger */
+ private $deprecationLogger;
/** @var bool */
private $disabled = false;
: function ( Exception $e ) {
trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
};
+ $this->deprecationLogger = isset( $params['deprecationLogger'] )
+ ? $params['deprecationLogger']
+ : function ( $msg ) {
+ trigger_error( $msg, E_USER_DEPRECATED );
+ };
foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) {
$this->$key = isset( $params[$key] ) ? $params[$key] : new NullLogger();
$server['connLogger'] = $this->connLogger;
$server['queryLogger'] = $this->queryLogger;
$server['errorLogger'] = $this->errorLogger;
+ $server['deprecationLogger'] = $this->deprecationLogger;
$server['profiler'] = $this->profiler;
$server['trxProfiler'] = $this->trxProfiler;
// Use the same agent and PHP mode for all DB handles
"cascadeprotected": "تمت حماية هذه الصفحة من التعديل لأنها مدمجة في {{PLURAL:$1||الصفحة التالية، والتي|الصفحتين التاليتين، واللتين|الصفحات التالية، والتي}} تم استعمال خاصية \"حماية الصفحات المدمجة\" {{PLURAL:$1||بها|بهما|بها}}:\n$2",
"namespaceprotected": "لا تمتلك الصلاحية لتعديل الصفحات في نطاق '''$1'''.",
"customcssprotected": "أنت لا تمتلك السماح لتعديل صفحة الCSS هذه، لأنها تحتوي على الإعدادات الشخصية لمستخدم آخر.",
+ "customjsonprotected": "ليست لديك صلاحية تحرير صفحة جسون هذه لأنها تحتوي على إعدادات شخصية لمستخدم آخر.",
"customjsprotected": "أنت لا تمتلك السماح لتعديل صفحة الجافاسكريبت هذه، لأنها تحتوي على الإعدادات الشخصية لمستخدم آخر.",
"mycustomcssprotected": "ليس لديك صلاحية تعديل هذه الصفحة للطرز المتراصة.",
+ "mycustomjsonprotected": "ليست لديك صلاحية تحرير صفحة جسون هذه",
"mycustomjsprotected": "ليس لديك صلاحية تعديل صفحة جافاسكربت هذه.",
"myprivateinfoprotected": "ليس لديك صلاحية تعديل معلوماتك الخاصة.",
"mypreferencesprotected": "ليس لديك صلاحية تعديل تفضيلاتك.",
"savechanges": "احفظ التغييرات",
"publishpage": "نشر الصفحة",
"publishchanges": "نشر التغييرات",
+ "savearticle-start": "احفظ الصفحة…",
+ "savechanges-start": "حفظ التغييرات...",
+ "publishpage-start": "نشر الصفحة...",
+ "publishchanges-start": "نشر التغييرات...",
"preview": "عرض مسبق",
"showpreview": "أظهر معاينة",
"showdiff": "عرض التغييرات",
"blocked-notice-logextract": "هذا المستخدم ممنوع حاليا.\nآخر مدخلة في سجل المنع موفرة بالأسفل كمرجع:",
"clearyourcache": "<strong>ملاحظة:</strong> بعد الحفظ، أنت قد تحتاج إلى إفراغ الكاش الخاص بمتصفحك لرؤية التغييرات.\n* <strong>فايرفوكس / سافاري:</strong> أمسك <em>Shift</em> أثناء ضغط <em>Reload</em>، أو اضغط على إما <em>Ctrl-F5</em> أو <em>Ctrl-R</em> (<em>⌘-R</em> على ماك)\n* <strong>جوجل كروم:</strong> اضغط <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> على ماك)\n* <strong>إنترنت إكسبلورر:</strong> أمسك <em>Ctrl</em> أثناء ضغط <em>Refresh</em>، أو اضغط <em>Ctrl-F5</em>\n* <strong>أوبرا:</strong> اذهب إلى <em>Menu → Settings</em> (<em>Opera → Preferences</em> على ماك) ثم إلى <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
"usercssyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة CSS الجديد قبل حفظ الصفحة.",
+ "userjsonyoucanpreview": "<strong>نصيحة:</strong> استخدم الزر \"{{int:showpreview}}\" لاختبار جسون الجديد قبل الحفظ.",
"userjsyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة جافاسكربت الجديدة قبل حفظ الصفحة.",
"usercsspreview": "'''تذكر أنك تقوم بعرض الأنماط المتراصة (CSS) الخاصة بك فقط\nلم يتم حفظها بعد!'''",
+ "userjsonpreview": "<strong>تذكر أنك تختبر/تستعرض تهيئة جسون للمستخدم فقط،\nلم يتم حفظها بعد!</strong>",
"userjspreview": "'''تذكر أنك فقط تجرب/تعاين جافاسكربت.'''\n'''لم يتم الحفظ بعد!'''",
"sitecsspreview": "''' تذكر أنك فقط في وضع المعاينة لهذا CSS ''' \n''' ولم يتم حفظ الصفحة بعد! '''",
+ "sitejsonpreview": "<strong>تذكر أنك تقوم بمعاينة تهيئة جسون هذه فقط،\nلم يتم حفظها بعد!</strong>",
"sitejspreview": "''' تذكر أنك فقط في وضع المعاينة لكود JavaScript هذا''' \n''' ولم يتم حفظه بعد! '''",
- "userinvalidconfigtitle": "'''تحذير:''' لا توجد واجهة \"$1\".\nتذكر أن ملفات ال.css و ال.js تستخدم حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
+ "userinvalidconfigtitle": "<strong>تحذير:</strong> T لا توجد واجهة \"$1\".\nصفحات Custom .css و.json و.js تستخدم حروفا صغيرة في العنوان، مثل {{ns:user}}:Foo/vector.css على عكس {{ns:user}}:Foo/Vector.css.",
"updated": "(محدثة)",
"note": "'''ملاحظة:'''",
"previewnote": "'''تذكر أن هذه مجرد معاينة أولية.'''\nلم تحفظ تغييراتك إلى الآن!",
"default": "افتراضي",
"prefs-files": "ملفات",
"prefs-custom-css": "CSS مخصص",
+ "prefs-custom-json": "جسون مخصص",
"prefs-custom-js": "جافاسكربت مخصص",
- "prefs-common-config": "CSS وجافاسكربت مشترك لجميع الواجهات:",
+ "prefs-common-config": "جافاسكربت/CSS/JSON مشترك لجميع الواجهات:",
"prefs-reset-intro": "يمكنك استخدام هذه الصفحة لإعادة تفضيلاتك للحالة الافتراضية للموقع.\nلن تستطيع استرجاع الحالة السابقة.",
"prefs-emailconfirm-label": "تأكيد البريد الإلكتروني:",
"youremail": "البريد:",
"right-editcontentmodel": "عدل طريقة محتوى صفحة",
"right-editinterface": "تعديل واجهة المستخدم",
"right-editusercss": "تعديل ملفات CSS للمستخدمين الآخرين",
+ "right-edituserjson": "تعديل ملفات جسون للمستخدمين الآخرين",
"right-edituserjs": "تعديل ملفات جافاسكريبت للمستخدمين الآخرين",
"right-editmyusercss": "تعديل ملفات CSS للمستخدم نفسه",
+ "right-editmyuserjson": "تعديل ملفات جسون للمستخدم نفسه",
"right-editmyuserjs": "تعديل ملفات جافاسكربت للمستخدم نفسه",
"right-viewmywatchlist": "عرض قائمة مراقبتك",
"right-editmywatchlist": "حرر قائمة مراقبتك. لاحظ أن بعض الإجراءات لا تزال تضيف الصفحات حتى بدون هذا الحق.",
"grant-createaccount": "إنشاء حسابات",
"grant-createeditmovepage": "إنشاء وتعديل ونقل الصفحات",
"grant-delete": "حذف الصفحات والمراجعات ومدخلات السجلات",
- "grant-editinterface": "تعديل نطاق ميدياويكي والCSS/JavaScript الخاصة بالمستخدم",
- "grant-editmycssjs": "تعديل الCSS/JavaScript الخاصة بحسابك",
+ "grant-editinterface": "تعديل نطاق ميدياويكي وCSS/جافا سكريت المستخدم",
+ "grant-editmycssjs": "تعديل CSS/جافا سكريت/جسون الخاصة بحسابك",
"grant-editmyoptions": "تعديل تفضيلاتك",
"grant-editmywatchlist": "تعديل قائمة مراقبتك",
"grant-editpage": "تعديل صفحات موجودة",
"group-bot.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على البوتات فقط */",
"group-sysop.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على الإداريين فقط */",
"group-bureaucrat.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على البيروقراطيين فقط */",
+ "common.json": "/* سيتم تحميل أي جسون هنا لجميع المستخدمين في كل تحميل للصفحة. */",
"common.js": "/* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */",
"group-autoconfirmed.js": "/* أي جافاسكريبت هنا سيتم تحميلها للمستخدمين المؤكدين تلقائيا فقط */",
"group-user.js": "/* أي JavaScript هنا سيتم تحميله للمستخدمين المسجلين فقط */",
"unlinkaccounts-success": "الحساب تم فك وصله.",
"authenticationdatachange-ignored": "تغيير بيانات التحقق لم يتم التعامل معه. ربما لم يتم ضبط موفر؟",
"userjsispublic": "من فضلك لاحظ: صفحات الجافاسكريبت الفرعية لا ينبغي أن تحتوي غلى بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين.",
+ "userjsonispublic": "الرجاء ملاحظة أنه: يجب ألا تحتوي الصفحات الفرعية لجسون على بيانات سرية لأنها قابلة للعرض من قبل المستخدمين الآخرين.",
"usercssispublic": "من فضل لاحظ: صفحات الCSS الفرعية لا ينبغي أن تحتوي على بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين.",
"restrictionsfield-badip": "عنوان أيبي أو نطاق غير صحيح: $1",
"restrictionsfield-label": "نطاقات الأيبي المسموح بها:",
"publishchanges": "Апублікаваць зьмены",
"savearticle-start": "Захаваць старонку…",
"savechanges-start": "Захаваць зьмены…",
+ "publishpage-start": "Апублікаваць старонку…",
"preview": "Папярэдні прагляд",
"showpreview": "Праглядзець",
"showdiff": "Паказаць зьмены",
"grant-editinterface": "Рэдагаваньне прасторы назваў MediaWiki і CSS/JSON/JavaScript удзельніка",
"grant-editmycssjs": "Рэдагаваньне вашага CSS/JSON/JavaScript",
"grant-editmyoptions": "Рэдагаваньне вашых наладаў удзельніка",
- "grant-editmywatchlist": "Рэдагаваць ваш сьпіс назіраньня",
+ "grant-editmywatchlist": "Рэдагаваньне вашага сьпісу назіраньня",
"grant-editpage": "Рэдагаваць існыя старонкі",
"grant-editprotected": "Рэдагаваць абароненыя старонкі",
"grant-highvolume": "Рэдагаваньне з высокай інтэнсіўнасьцю",
"wrongpasswordempty": "Не е въведена парола.\nОпитайте отново.",
"passwordtooshort": "Необходимо е паролата да съдържа поне {{PLURAL:$1|1 знак|$1 знака}}.",
"passwordtoolong": "Паролата не може да бъде по-дългa от {{PLURAL:$1|1 знак|$1 знака}}.",
- "passwordtoopopular": "ЧеÑ\81Ñ\82о използвани паÑ\80оли не могаÑ\82 да бÑ\8aдаÑ\82 ползвани. Ð\9cолÑ\8f, избеÑ\80еÑ\82е по-Ñ\83никална паÑ\80ола.",
+ "passwordtoopopular": "ЧеÑ\81Ñ\82о използвани паÑ\80оли не могаÑ\82 да бÑ\8aдаÑ\82 ползвани. Ð\9cолÑ\8f, избеÑ\80еÑ\82е паÑ\80ола, коÑ\8fÑ\82о е по-Ñ\82Ñ\80Ñ\83дна за познаване.",
"password-name-match": "Паролата Ви трябва да се различава от потребителското Ви име.",
"password-login-forbidden": "Използването на това потребителско име и парола е забранено.",
"mailmypassword": "Възстановяване на парола",
"userjspreview": "<strong>Не забравяйте, че това е само изпробване/предварителен преглед на кода на JavaScript.\nСтраницата все още не е съхранена!</strong>",
"sitecsspreview": "<strong>Не забравяйте, че това е само предварителен преглед на този CSS.\nТой все още не е съхранен!</strong>",
"sitejspreview": "<strong>Не забравяйте, че това е само предварителен преглед на този JavaScript код.\nТой все още не е съхранен!</strong>",
- "userinvalidconfigtitle": "<strong>Ð\92нимание:</strong> Ð\9dе Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва облик â\80\9e$1â\80\9c.\nÐ\9dеобÑ\85одимо е да Ñ\81е знае, Ñ\87е именаÑ\82а на поÑ\82Ñ\80ебиÑ\82елÑ\81киÑ\82е ви Ñ\81Ñ\82Ñ\80аниÑ\86и за CSS и JavaScript Ñ\82Ñ\80Ñ\8fбва да Ñ\81е Ñ\81Ñ\8aÑ\81Ñ\82оÑ\8fÑ\82 оÑ\82 малки бÑ\83кви, напÑ\80имеÑ\80: â\80\9e{{ns:user}}:Ð\98ван/vector.cssâ\80\9c (а не â\80\9e{{ns:user}}:Ð\98ван/Vector.cssâ\80\9c).",
+ "userinvalidconfigtitle": "<strong>Ð\92нимание:</strong> Ð\9dе Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва облик â\80\9e$1â\80\9c.\nÐ\98менаÑ\82а на поÑ\82Ñ\80ебиÑ\82елÑ\81ки .css, .json и .js Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\82Ñ\80Ñ\8fбва да използваÑ\82 малки бÑ\83кви, напÑ\80имеÑ\80: {{ns:user}}:Ð\98ван/vector.css (а не {{ns:user}}:Ð\98ван/Vector.css).",
"updated": "(обновена)",
"note": "<strong>Забележка:</strong>",
"previewnote": "<strong>Обърнете внимание, че това е само предварителен преглед.</strong>\nПромените все още не са съхранени!",
"expansion-depth-exceeded-category-desc": "Страницата превишава максимално допустимата дълбочина на разгръщане.",
"expansion-depth-exceeded-warning": "Страницата е превишила разрешената дълбочина на разгръщане",
"parser-unstrip-loop-warning": "Открито е ''unstrip'' зацикляне",
- "unstrip-depth-warning": "''Unstrip'' лимита на рекурсия превишава ($1)",
+ "unstrip-depth-warning": "Превишено ограничение на дълбочина ($1)",
"undo-success": "Редакцията може да бъде върната.\nПрегледайте долното сравнение и се уверете, че наистина искате да го направите. След това съхранете страницата, за да извършите връщането.",
"undo-failure": "Редакцията не може да бъде върната поради конфликтни междинни редакции.",
"undo-norev": "Редакцията не може да бъде върната, тъй като не съществува или е била изтрита.",
"filerevert-intro": "Възвръщане на <strong>[[Media:$1|$1]]</strong> към [$4 версията от $3, $2].",
"filerevert-comment": "Причина:",
"filerevert-defaultcomment": "Възвръщане към версия от $2, $1 ($3)",
- "filerevert-submit": "Ð\92Ñ\8aзвÑ\80Ñ\8aÑ\89ане",
+ "filerevert-submit": "Връщане",
"filerevert-success": "Файлът <strong>[[Media:$1|$1]]</strong> беше възвърнат към [$4 версия от $3, $2].",
"filerevert-badversion": "Не съществува предишна локална версия на файла със зададения времеви отпечатък.",
"filedelete": "Изтриване на $1",
"protect_expiry_invalid": "Невалиден срок на изтичане.",
"protect_expiry_old": "Срокът на изтичане е минал.",
"protect-unchain-permissions": "Позволяване на по-нататъшни възможности за защита",
- "protect-text": "Тук можете да прегледате и промените нивото на защита на страницата '''$1'''.",
- "protect-locked-blocked": "Не можете да променяте нивата на защита на страниците, докато сте блокиран(а). Текущите настройки за страницата „'''$1'''“ са:",
- "protect-locked-dblock": "Нивата на защита на страниците не могат да бъдат променяни, защото базата от данни е заключена. Ето текущите настройки за страницата „'''$1'''“:",
+ "protect-text": "Тук можете да прегледате и промените нивото на защита на страницата <strong>$1</strong>.",
+ "protect-locked-blocked": "Не можете да променяте нивата на защита на страниците, докато сте блокиран(а).\nТекущите настройки за страницата <strong>$1</strong> са:",
+ "protect-locked-dblock": "Нивата на защита на страниците не могат да бъдат променяни, защото базата от данни е заключена.\nЕто текущите настройки за страницата <strong>$1</strong>:",
"protect-locked-access": "Нямате правото да променяте нивата на защита на страниците. Ето текущите настройки за страницата „'''$1'''“:",
"protect-cascadeon": "Тази страница е защитена против редактиране, защото е включена в {{PLURAL:$1|следната страница, която от своя страна има|следните страници, които от своя страна имат}} каскадна защита.\nМожете да промените нивото на защита на страницата, но това няма да повлияе върху каскадната защита.",
"protect-default": "Позволяване за всички потребители",
"missing-revision": "АгӀона «{{FULLPAGENAME}}» верси $1 яц.\n\nИштта хуьйла ширъелла дӀаяьккхина агӀонан хьажораган дихьа делча.\nМа-дара хила мега [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дӀайаьхарш йолу тептар] чохь.",
"userpage-userdoesnotexist": "ХӀокху декъашхочун «<nowiki>$1</nowiki>» хьесапан дӀаяздар дац. Тешна хила, хьуна баккъалла лаьий кхолла а, я хӀокху агӀонан хийцам бан а.",
"userpage-userdoesnotexist-view": "«$1» иштта декъашхочун дӀаяздар дац.",
- "blocked-notice-logextract": "Ð¥Ó\80аÑ\80а декÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н дÓ\80аÑ\8fздаÑ\80 блокÑ\82оÑ\8cÑ\85на дÑ\83.\nÐ\9bаÑ\85аÑ\85Ñ\8c гойÑ\82Ñ\83 блокÑ\82оÑ\85аÑ\80ийн Ñ\82епÑ\82аÑ\80 Ñ\87Ñ\83Ñ\80а Ñ\82Ó\80аÑ\8cÑ\85Ñ\85Ñ\8cаÑ\80леÑ\80а дÓ\80аÑ\8fздаÑ\80:",
+ "blocked-notice-logextract": "ХӀара декъашхочун дӀаяздар блоктоьхна ду.\nЛахахь гойту блоктохарийн тептар чура тӀаьххьара дӀаяздар:",
"clearyourcache": "'''Билгалдаккхар.''' Ӏалашйинчул тӀехьа хийцамаш га браузеран кэш цӀанъян езаш хила мега.\n* '''Firefox / Safari:''' ''Shift'' цӀе йолу пиллиг лаьцна битна, гӀирсийн панелан тӀера тӀетаӀе ''Карлаяккха'' я ''Ctrl-F5'' я ''Ctrl-R'' (''⌘-R'' Mac тӀехь)\n* '''Google Chrome:''' ТӀетаӀе ''Ctrl-Shift-R'' (''⌘-Shift-R'' Mac тӀехь)\n* '''Internet Explorer:''' ''Ctrl'' лаьцна йитан, тӀетаӀе ''Карлаяккха'' я тӀетаӀе ''Ctrl-F5''\n* '''Opera:''' Кэш цӀанъяр харжа меню ''Инструменты → Настройки'' чохь",
"usercssyoucanpreview": "'''ДӀаалар.''' ТӀетаӀае кнопка «{{int:showpreview}}», хьажа хьай керла CSS-файл Ӏалаш яле.",
"userjsyoucanpreview": "'''ДӀаалар.''' ТӀетаӀае кнопка «{{int:showpreview}}», хьажа хьай керла JS-файл Ӏалаш яле.",
"sp-contributions-logs": "тéптарш",
"sp-contributions-talk": "дийцаре",
"sp-contributions-userrights": "декъашхочун бакъонашна урхалладар",
- "sp-contributions-blocked-notice": "Ð¥Ó\80аÑ\80а декÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н дÓ\80аÑ\8fздаÑ\80 блокÑ\82оÑ\8cÑ\85на дÑ\83.\nÐ\9bаÑ\85аÑ\85Ñ\8c гойÑ\82Ñ\83 блокÑ\82оÑ\85аÑ\80ийн Ñ\82епÑ\82аÑ\80 Ñ\87Ñ\83Ñ\80а Ñ\82Ó\80аÑ\8cÑ\85Ñ\85Ñ\8cаÑ\80леÑ\80а дÓ\80аÑ\8fздаÑ\80:",
- "sp-contributions-blocked-notice-anon": "Ð¥Ó\80аÑ\80а IP-адÑ\80еÑ\81 Ñ\85Ó\80инÑ\86а блокÑ\82оÑ\8cÑ\85на дÑ\83.\nÐ\9bаÑ\85аÑ\85Ñ\8c гойÑ\82Ñ\83 блокÑ\82оÑ\85аÑ\80ийн Ñ\82епÑ\82аÑ\80 Ñ\87Ñ\83Ñ\80а Ñ\82Ó\80аÑ\8cÑ\85Ñ\85Ñ\8cаÑ\80леÑ\80а дÓ\80аÑ\8fздаÑ\80:",
+ "sp-contributions-blocked-notice": "ХӀара декъашхочун дӀаяздар блоктоьхна ду.\nЛахахь гойту блоктохарийн тептар чура тӀаьххьара дӀаяздар:",
+ "sp-contributions-blocked-notice-anon": "ХӀара IP-адрес хӀинца блоктоьхна ду.\nЛахахь гойту блоктохарийн тептар чура тӀаьххьара дӀаяздар:",
"sp-contributions-search": "Къинхьегам лахар",
"sp-contributions-username": "IP-адрес я декъашхочун цӀе:",
"sp-contributions-toponly": "Гайта тӀаьххьарлера хийцамаш",
"cascadeprotected": "Tato stránka je zamčena, neboť je vložena na {{PLURAL:$1|následující stránku, zamčenou|následující stránky, zamčené}} kaskádovým zámkem:\n$2",
"namespaceprotected": "Nemáte povoleno editovat stránky ve jmenném prostoru <strong>$1</strong>.",
"customcssprotected": "Nemáte povoleno editovat tuto stránku s CSS, protože obsahuje osobní nastavení jiného uživatele.",
+ "customjsonprotected": "Nemáte povoleno editovat tuto stránku s JSONem, protože obsahuje osobní nastavení jiného uživatele.",
"customjsprotected": "Nemáte povoleno editovat tuto stránku s JavaScriptem, protože obsahuje osobní nastavení jiného uživatele.",
"mycustomcssprotected": "Nemáte oprávnění editovat tuto stránku s CSS.",
+ "mycustomjsonprotected": "Nemáte oprávnění editovat tuto stránku s JSONem.",
"mycustomjsprotected": "Nemáte oprávnění editovat tuto stránku s JavaScriptem.",
"myprivateinfoprotected": "Nemáte oprávnění měnit své soukromé údaje.",
"mypreferencesprotected": "Nemáte oprávnění změnit svá nastavení.",
"blocked-notice-logextract": "{{GENDER:$1|Tento uživatel|Tato uživatelka}} je momentálně {{GENDER:$1|zablokován|zablokována}}.\nZde je pro přehled zobrazen nejnovější záznam z knihy zablokování:",
"clearyourcache": "<strong>Poznámka:</strong> Po uložení musíte smazat cache vašeho prohlížeče, jinak změny neuvidíte.\n* <strong>Firefox / Safari:</strong> Při kliknutí na <em>Aktualizovat</em> držte <em>Shift</em> nebo stiskněte <em>Ctrl-F5</em> nebo <em>Ctrl-R</em> (na Macu <em>⌘-R</em>)\n* <strong>Google Chrome:</strong> Stiskněte <em>Ctrl-Shift-R</em> (na Macu <em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong> Při kliknutí na <em>Aktualizovat</em> držte <em>Ctrl</em> nebo stiskněte <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Jděte do <em>Menu → Nastavení</em> (na Macu <em>Opera → Nastavení</em>) a tam pak <em>Soukromí & bezpečnost → Vymazat údaje o prohlížení → Obrázky a soubory z cache</em>",
"usercssyoucanpreview": "<strong>Tip:</strong> Použijte tlačítko „{{int:showpreview}}“ k testování vašeho nového CSS před uložením.",
+ "userjsonyoucanpreview": "<strong>Tip:</strong> Použijte tlačítko „{{int:showpreview}}“ k testování vašeho nového JSONu před uložením.",
"userjsyoucanpreview": "<strong>Tip:</strong> Použijte tlačítko „{{int:showpreview}}“ k testování vašeho nového JavaScriptu před uložením.",
"usercsspreview": "<strong>Pamatujte, že si prohlížíte jen náhled vašeho uživatelského CSS, jelikož dosud nebyl uložen!</strong>",
+ "userjsonpreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled svého uživatelského JSONu, jelikož dosud nebyl uložen!</strong>",
"userjspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled svého uživatelského JavaScriptu, jelikož dosud nebyl uložen!</strong>",
"sitecsspreview": "<strong>Pamatujte, že si prohlížíte jen náhled tohoto CSS, jelikož dosud nebylo uloženo!</strong>",
+ "sitejsonpreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled tohoto JSONu, jelikož dosud nebyl uložen!</strong>",
"sitejspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled tohoto JavaScriptu, jelikož dosud nebyl uložen!</strong>",
- "userinvalidconfigtitle": "<strong>Varování:</strong> Vzhled „$1“ neexistuje. Nezapomeňte, že uživatelské .css a .js soubory používají malá písmena, např. {{ns:user}}:{{BASEPAGENAME}}/vector.css, nikoli {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
+ "userinvalidconfigtitle": "<strong>Varování:</strong> Vzhled „$1“ neexistuje. Nezapomeňte, že uživatelské .css, .json a .js soubory používají malá písmena, např. {{ns:user}}:{{BASEPAGENAME}}/vector.css, nikoli {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
"updated": "(Změna uložena)",
"note": "<strong>Poznámka:</strong>",
"previewnote": "<strong>Pamatujte, že toto je pouze náhled.</strong>\nZměny dosud nebyly uloženy!",
"default": "implicitní",
"prefs-files": "Soubory",
"prefs-custom-css": "Uživatelské CSS",
+ "prefs-custom-json": "Uživatelský JSON",
"prefs-custom-js": "Uživatelský JavaScript",
- "prefs-common-config": "Sdílené CSS/JavaScript pro všechny styly:",
+ "prefs-common-config": "Sdílené CSS/JSON/JavaScript pro všechny styly:",
"prefs-reset-intro": "Pomocí této stránky můžete všechna nastavení vrátit na implicitní hodnoty.\nTuto operaci nelze vrátit zpět.",
"prefs-emailconfirm-label": "Ověření e-mailu:",
"youremail": "E-mail:",
"right-editcontentmodel": "Editace modelu obsahu stránky",
"right-editinterface": "Editace zpráv uživatelského rozhraní",
"right-editusercss": "Editace CSS souborů jiných uživatelů",
+ "right-edituserjson": "Editace souborů s JSONem jiných uživatelů",
"right-edituserjs": "Editace JavaScriptových souborů jiných uživatelů",
"right-editmyusercss": "Editace vlastních uživatelských CSS souborů",
+ "right-editmyuserjson": "Editace vlastní uživatelských souborů s JSONem",
"right-editmyuserjs": "Editace vlastních uživatelských JavaScriptových souborů",
"right-viewmywatchlist": "Prohlížení vlastního seznamu sledovaných stránek",
"right-editmywatchlist": "Editace vlastního seznamu sledovaných stránek. Uvědomte si, že některé akce do něj mohou přidat stránky i bez tohoto oprávnění.",
"grant-createaccount": "Zakládat účty",
"grant-createeditmovepage": "Vytvářet, editovat a přesouvat stránky",
"grant-delete": "Mazat stránky, revize a protokolovací záznamy",
- "grant-editinterface": "Editovat jmenný prostor MediaWiki a uživatelské CSS/JavaScript",
- "grant-editmycssjs": "Editovat váš uživatelský CSS/JavaScript",
+ "grant-editinterface": "Editovat jmenný prostor MediaWiki a uživatelské CSS/JSON/JavaScript",
+ "grant-editmycssjs": "Editovat váš uživatelský CSS/JSON/JavaScript",
"grant-editmyoptions": "Změna vašich uživatelských nastavení",
"grant-editmywatchlist": "Upravovat váš seznam sledovaných stránek",
"grant-editpage": "Editovat existující stránky",
"unlinkaccounts-success": "Propojení účtu bylo zrušeno.",
"authenticationdatachange-ignored": "Změna autentizačních údajů nebyla zpracována. Možná není nakonfigurován žádný poskytovatel?",
"userjsispublic": "Uvědomte si prosím, že podstránky s JavaScriptem by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům.",
+ "userjsonispublic": "Uvědomte si prosím, že podstránky s JSONem by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům.",
"usercssispublic": "Uvědomte si prosím, že podstránky s CSS by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům.",
"restrictionsfield-badip": "Neplatná IP adresa nebo rozsah: $1",
"restrictionsfield-label": "Povolené rozsahy IP adres:",
"apisandbox-dynamic-error-exists": "Ein Parameter mit dem Namen „$1“ ist bereits vorhanden.",
"apisandbox-deprecated-parameters": "Veraltete Parameter",
"apisandbox-fetch-token": "Den Token automatisch ausfüllen",
+ "apisandbox-add-multi": "Hinzufügen",
"apisandbox-submit-invalid-fields-title": "Einige Felder sind ungültig",
"apisandbox-submit-invalid-fields-message": "Korrigiere bitte die markierten Felder und versuche es erneut.",
"apisandbox-results": "Ergebnisse",
"tag-mw-removed-redirect": "Widjerfeerang wechnimen",
"tag-mw-changed-redirect-target": "Widjerfeerang feranert",
"tag-mw-rollback": "Turagsaat",
+ "tag-mw-undo": "Turag saaten",
"tags-title": "Kääntiaken",
"tags-intro": "Det sidj wiset kääntiaken, diar för't bewerkin brükt wurd, an wat jo men.",
"tags-tag": "Kääntiaken-nööm",
"viewsource": "Wè tèks sours",
"viewsource-title": "Wè sours-a di $1",
"actionthrottled": "Aksyon limité",
+ "actionthrottledtext": "Pou briga kont abi-ya, itilizasyon-an di sa aksyon sa limité à roun sèrten nonm di fwè annan roun laps di tan asé kourt é zòt dépasé sa limit.\nSouplé, éséyé òkò annan tchèk minout.",
+ "protectedpagetext": "Sa paj té protéjé pou anpéché so modifikasyon oben dé ròt aksyon.",
"viewsourcetext": "Zòt pé wè é kopyé kontni di sa paj.",
+ "viewyourtext": "Zòt pouvé wè ké kopyé kontni-a di <strong>zòt modifikasyon</strong> à sa paj.",
+ "protectedinterface": "Sa paj ka fourni tèks d'entèrfas pou lojisyèl-a asou sa wiki é sa protéjé pou évité abi-ya.\nPou ajouté oben modifyé dé amòrfwazaj asou tout wiki, souplé, itilizé [https://translatewiki.net/ translatewiki.net], projè-a di réjyonalizasyon di MediaWiki.",
+ "editinginterface": "<strong>Panga :</strong> zòt ka modifiyé oun paj itilizé pou kréyé tèks-a di lojisyèl.\nChanjman-yan asou sa paj ké répèrkité asou aparans di entèrfas itilizatò pou ròt itilizatò-ya di sa wiki.",
+ "translateinterface": "Pou ajouté oben modifyé dé amòrfwazaj pou tout wiki, souplé, itilizé [https://translatewiki.net/ translatewiki.net], projè-a di lokalizasyon lengwistik di MediaWiki.",
+ "cascadeprotected": "Sa paj protéjé kont modifikasyon-yan pas li sa transkliz pa {{PLURAL:$1|paj-a ki ka swiv, ki té protéjé|paj-ya ki ka swiv, ki té protéjé}} ké lòpsyon « protèksyon an kaskad » aktivé :\n$2",
+ "namespaceprotected": "Zòt pa gen pèrmisyon-an di modifyé paj-ya di lèspas di non « <strong>$1</strong> ».",
"userlogin-yourname": "Non di itilizatò",
"userlogin-yourname-ph": "Antré zòt non di itilizatò",
"userlogin-yourpassword": "Mo di pas",
"userpage-userdoesnotexist-view": "חשבון המשתמש \"$1\" אינו רשום.",
"blocked-notice-logextract": "{{GENDER:$1|המשתמש הזה חסום|המשתמשת הזו חסומה}} כרגע.\nהפעולה האחרונה ביומן החסימות מוצגת להלן:",
"clearyourcache": "<strong>הערה:</strong> לאחר השמירה, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.\n* <strong>פיירפוקס / ספארי:</strong> להחזיק את המקש <em>Shift</em> בעת לחיצה על <strong>טעינה מחדש</strong> (Reload), או ללחוץ על צירוף המקשים <em>Ctrl-F5</em> או <em>Ctrl-R</em> (במחשב מק: <em dir=\"ltr\">⌘-R</em>).\n* <strong>גוגל כרום:</strong> ללחוץ על צירוף המקשים <em>Ctrl-Shift-R</em> (במחשב מק: <em dir=\"ltr\">⌘-Shift-R</em>).\n* <strong>אינטרנט אקספלורר:</strong> להחזיק את המקש <em>Ctrl</em> בעת לחיצה על <strong>רענן</strong> (Refresh), או ללחוץ על צירוף המקשים <em>Ctrl-F5</em>.\n* <strong>אופרה:</strong> לפתוח <em>תפריט ← הגדרות</em> (במחשב מק: <em>Opera ← העדפות</em>) ואז ללחוץ על <em>פרטיות ואבטחה ← מחק היסטוריית גלישה ← Cached images and files</em>.",
- "usercssyoucanpreview": "<strong>עצ×\94:</strong> ×\94שת×\9eש×\95 ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת ×\92×\99×\9c×\99×\95×\9f ×\94Ö¾CSS ×\94×\97×\93ש ש×\9c×\9b×\9d לפני השמירה.",
- "userjsonyoucanpreview": "<strong>עצ×\94:</strong> ×\94שת×\9eש×\95 ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת ×\93×£ ×\94Ö¾JSON ×\94×\97×\93ש ש×\9c×\9b×\9d לפני השמירה.",
- "userjsyoucanpreview": "<strong>עצ×\94:</strong> ×\94שת×\9eש×\95 ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת סקר×\99פ×\98 ×\94Ö¾JavaScript ×\94×\97×\93ש ש×\9c×\9b×\9d לפני השמירה.",
- "usercsspreview": "<strong>זִכרו שזו רק תצוגה מקדימה של גיליון ה־CSS שלכם.\nהוא עדיין לא נשמר!</strong>",
- "userjsonpreview": "<strong>זִכרו שזו רק בדיקה/תצוגה מקדימה של הגדרות ה־JSON שלכם.\nהן עדיין לא נשמרו!</strong>",
- "userjspreview": "<strong>זִכרו שזו רק בדיקה/תצוגה מקדימה של סקריפט ה־JavaScript שלכם.\nהוא עדיין לא נשמר!</strong>",
- "sitecsspreview": "<strong>זִכרו שזו רק תצוגה מקדימה של גיליון ה־CSS הזה.\nהוא עדיין לא נשמר!</strong>",
- "sitejsonpreview": "<strong>זִכרו שזו רק תצוגה מקדימה של הגדרות ה־JSON האלה.\nהן עדיין לא נשמרו!</strong>",
- "sitejspreview": "<strong>זִכרו שזו רק תצוגה מקדימה של סקריפט ה־JavaScript הזה.\nהוא עדיין לא נשמר!</strong>",
- "userinvalidconfigtitle": "<strong>אזהרה:</strong> העיצוב \"$1\" אינו קיים.\nדפי .css, דפי .json, ודפי .js מותאמים אישית משתמשים בכותרת עם אותיות קטנות – למשל, {{ns:user}}:דוגמה/vector.css ולא {{ns:user}}:דוגמה/Vector.css.",
+ "usercssyoucanpreview": "<strong>עצ×\94:</strong> ×\91×\90פשר×\95ת×\9a ×\9c×\94שת×\9eש ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת ×\92×\99×\9c×\99×\95×\9f ×\94Ö¾CSS ×\94×\97×\93ש ש×\9c×\9a לפני השמירה.",
+ "userjsonyoucanpreview": "<strong>עצ×\94:</strong> ×\91×\90פשר×\95ת×\9a ×\9c×\94שת×\9eש ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת ×\93×£ ×\94Ö¾JSON ×\94×\97×\93ש ש×\9c×\9a לפני השמירה.",
+ "userjsyoucanpreview": "<strong>עצ×\94:</strong> ×\91×\90פשר×\95ת×\9a ×\9c×\94שת×\9eש ×\91×\9bפת×\95ר \"{{int:showpreview}}\" ×\9b×\93×\99 ×\9c×\91×\97×\95×\9f ×\90ת סקר×\99פ×\98 ×\94Ö¾JavaScript ×\94×\97×\93ש ש×\9c×\9a לפני השמירה.",
+ "usercsspreview": "<strong>זו רק תצוגה מקדימה של גיליון ה־CSS שלך.\nהוא עדיין לא נשמר!</strong>",
+ "userjsonpreview": "<strong>זו רק בדיקה/תצוגה מקדימה של הגדרות ה־JSON שלך.\nהן עדיין לא נשמרו!</strong>",
+ "userjspreview": "<strong>זו רק בדיקה/תצוגה מקדימה של סקריפט ה־JavaScript שלך.\nהוא עדיין לא נשמר!</strong>",
+ "sitecsspreview": "<strong>זו רק תצוגה מקדימה של גיליון ה־CSS הזה.\nהוא עדיין לא נשמר!</strong>",
+ "sitejsonpreview": "<strong>זו רק תצוגה מקדימה של הגדרות ה־JSON האלה.\nהן עדיין לא נשמרו!</strong>",
+ "sitejspreview": "<strong>זו רק תצוגה מקדימה של סקריפט ה־JavaScript הזה.\nהוא עדיין לא נשמר!</strong>",
+ "userinvalidconfigtitle": "<strong>אזהרה:</strong> העיצוב \"$1\" אינו קיים.\nדפי <span dir=\"ltr\">.css</span>, דפי <span dir=\"ltr\"><span dir=\"ltr\">.js</span>on</span> ודפי <span dir=\"ltr\">.js</span> מותאמים אישית משתמשים בכותרת עם אותיות קטנות – למשל, {{ns:user}}:דוגמה/vector.css ולא {{ns:user}}:דוגמה/Vector.css.",
"updated": "(מעודכן)",
"note": "'''הערה:'''",
"previewnote": "<strong>{{GENDER:|זכור|זִכרי|זִכרו}} שזו רק תצוגה מקדימה.</strong>\nהשינויים {{GENDER:|שלך|שלך|שלכם}} עדיין לא נשמרו!",
"apisandbox-dynamic-error-exists": "פרמטר בשם \"$1\" כבר קיים.",
"apisandbox-deprecated-parameters": "פרמטרים מיושנים",
"apisandbox-fetch-token": "מילוי אוטומטי של האסימון",
+ "apisandbox-add-multi": "הוספה",
"apisandbox-submit-invalid-fields-title": "חלק מהשדות אינם תקינים",
"apisandbox-submit-invalid-fields-message": "אנא תקנו את השדות המסומנים ונסו שוב.",
"apisandbox-results": "תוצאות",
"sp-contributions-newbies": "Ցույց տալ միայն նորաստեղծ հաշիվներից կատարված ներդրումները",
"sp-contributions-newbies-sub": "Նոր մասնակցային հաշիվներից",
"sp-contributions-newbies-title": "Նոր մասնակիցների ներդրումներ",
- "sp-contributions-blocklog": "Արգելափակման տեղեկամատյան",
+ "sp-contributions-blocklog": "արգելափակման տեղեկամատյան",
"sp-contributions-deleted": "մասնակցի ջնջված ներդրում",
- "sp-contributions-uploads": "Բեռնումներ",
+ "sp-contributions-uploads": "բեռնումներ",
"sp-contributions-logs": "տեղեկամատյաններ",
"sp-contributions-talk": "քննարկում",
"sp-contributions-userrights": "մասնակիցների իրավունքների կառավարում",
"contribslink": "ներդրում",
"emaillink": "ուղարկել էլ. նամակ",
"autoblocker": "Դուք ավտոմատիկ արգելափակվել եք «$1» մասնակցի հետ ձեր IP-հասցեի համընկնելու պատճառով։ Նրա արգելափակման պատճառն է՝ «$2»։",
- "blocklogpage": "Արգելափակման տեղեկամատյան",
+ "blocklogpage": "արգելափակման տեղեկամատյան",
"blocklogentry": "արգելափակեց [[$1]] մասնակցին $2 տևողությամբ (Պատճառը՝ $3)",
"reblock-logentry": "փոխեց [[$1]] մասնակցի արգելափակումը՝ դարձնելով $2 $3",
"blocklogtext": "Սա մասնակիցների արգելափակման և արգելափակումից հանման տեղեկամատյանն է։\nԱվտոմատ կերպով արգելափակված IP-հասցեներն այստեղ ընդգրկված չեն։\nՏես [[Special:BlockList|այս պահին ակտիվ արգելափակումների ցանկը]]։",
"special-characters-title-endash": "ո գծիկ (en dash)",
"special-characters-title-emdash": "ա գծիկ (em dash)",
"special-characters-title-minus": "հանածի նշան",
+ "mw-widgets-dateinput-no-date": "Ամսաթիվն ընտրված չէ",
"mw-widgets-mediasearch-noresults": "Ոչինչ չի գտնվել",
+ "date-range-from": "Սկսած՝",
+ "date-range-to": "Մինչև՝",
"authmanager-create-from-login": "Հաշիվ ստեղծելու համար, խնդրում ենք լրացնել ստորև դաշտերը"
}
"revdelete-concurrent-change": "Impossibile modificare l'oggetto con data $1 $2 in quanto il suo stato è stato modificato da un altro utente mentre se ne tentava la modifica.",
"revdelete-only-restricted": "Errore nel nascondere l'oggetto datato $1, $2: non è possibile nascondere gli oggetti alla vista degli amministratori senza selezionare almeno un'altra delle opzioni di rimozione.",
"revdelete-reason-dropdown": "* Motivazioni più comuni per la cancellazione\n** Violazione di copyright\n** Commenti o informazioni personali inappropriate\n** Nome utente inappropriato\n** Informazione potenzialmente diffamatoria",
- "revdelete-otherreason": "Altra motivazione o motivazione aggiuntiva:",
+ "revdelete-otherreason": "Altri motivi/dettagli:",
"revdelete-reasonotherlist": "Altra motivazione",
"revdelete-edit-reasonlist": "Modifica le motivazioni per la cancellazione",
"revdelete-offender": "Autore della versione:",
"filedelete-success-old": "La versione del file '''[[Media:$1|$1]]''' del $2, $3 è stata cancellata.",
"filedelete-nofile": "Non esiste un file '''$1'''.",
"filedelete-nofile-old": "In archivio non ci sono versioni di '''$1''' con le caratteristiche indicate",
- "filedelete-otherreason": "Altra motivazione o motivazione aggiuntiva:",
+ "filedelete-otherreason": "Altri motivi/dettagli:",
"filedelete-reason-otherlist": "Altra motivazione",
"filedelete-reason-dropdown": "*Motivazioni più comuni per la cancellazione\n** Violazione di copyright\n** File duplicato",
"filedelete-edit-reasonlist": "Modifica le motivazioni per la cancellazione",
"deletionlog": "cancellazioni",
"reverted": "Ripristinata la versione precedente",
"deletecomment": "Motivo:",
- "deleteotherreason": "Altra motivazione o motivazione aggiuntiva:",
+ "deleteotherreason": "Altri motivi/dettagli:",
"deletereasonotherlist": "Altra motivazione",
"deletereason-dropdown": "* Motivazioni più comuni per la cancellazione\n** Spam\n** Vandalismo\n** Violazione di copyright\n** Richiesta dell'autore\n** Redirect rotto",
"delete-edit-reasonlist": "Modifica i motivi di cancellazione",
"recentchangeslinked-page": "താളിന്റെ പേര്:",
"recentchangeslinked-to": "തന്നിരിക്കുന്ന താളിലെ മാറ്റങ്ങൾക്കു പകരം ബന്ധപ്പെട്ട താളുകളിലെ മാറ്റങ്ങൾ കാണിക്കുക",
"recentchanges-page-added-to-category": "[[:$1]] വർഗ്ഗത്തിലേക്ക് ചേർത്തിരിക്കുന്നു",
- "recentchanges-page-added-to-category-bundled": "[[:$1]] താളàµ\81à´\82 à´\92à´ªàµ\8dà´ªà´\82 [[Special:WhatLinksHere/$1|{{PLURAL:$2|മറàµ\8dà´±àµ\8aà´°àµ\81 താളàµ\81à´\82|$2 താളàµ\81à´\95à´³àµ\81à´\82}}]] വർà´\97àµ\8dà´\97à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´\9aàµ\87ർതàµ\8dതിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81",
+ "recentchanges-page-added-to-category-bundled": "[[:$1]] താൾ വർà´\97àµ\8dà´\97à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´\9aàµ\87ർതàµ\8dതിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81, [[Special:WhatLinksHere/$1|à´\88 താൾ മറàµ\8dà´±àµ\8d താളàµ\81à´\95ളിൽ à´\89ൾപàµ\8dà´ªàµ\86à´\9fàµ\81à´¤àµ\8dതിയിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81]]",
"recentchanges-page-removed-from-category": "[[:$1]] വർഗ്ഗത്തിൽ നിന്ന് നീക്കംചെയ്തു",
- "recentchanges-page-removed-from-category-bundled": "[[:$1]] താളàµ\81à´\82 à´\92à´ªàµ\8dà´ªà´\82 {{PLURAL:$2|മറàµ\8dà´±àµ\8aà´°àµ\81 താളàµ\81à´\82|$2 താളàµ\81à´\95à´³àµ\81à´\82}} വർà´\97àµ\8dà´\97à´¤àµ\8dതിൽ നിനàµ\8dà´¨àµ\8d à´¨àµ\80à´\95àµ\8dà´\95à´\82à´\9aàµ\86à´¯àµ\8dതിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81",
+ "recentchanges-page-removed-from-category-bundled": "[[:$1]] താൾ വർà´\97àµ\8dà´\97à´¤àµ\8dതിൽ നിനàµ\8dà´¨àµ\8d à´¨àµ\80à´\95àµ\8dà´\95à´\82à´\9aàµ\86à´¯àµ\8dതിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81, [[Special:WhatLinksHere/$1|à´\88 താൾ മറàµ\8dà´±àµ\8d താളàµ\81à´\95ളിൽ à´\89ൾപàµ\8dà´ªàµ\86à´\9fàµ\81à´¤àµ\8dതിയിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81]]",
"autochange-username": "മീഡിയവിക്കി സ്വയംപ്രവർത്തിത മാറ്റം",
"upload": "അപ്ലോഡ്",
"uploadbtn": "പ്രമാണം അപ്ലോഡ് ചെയ്യുക",
"uploaded-script-svg": "അപ്ലോഡ് ചെയ്ത എസ്.വി.ജി. പ്രമാണത്തിൽ സ്ക്രിപ്റ്റ് ചെയ്യാവുന്ന ഭാഗമായ \"$1\" കണ്ടെത്തി.",
"uploaded-hostile-svg": "അപ്ലോഡ് ചെയ്ത എസ്.വി.ജി. പ്രമാണത്തിൽ സുരക്ഷിതമല്ലാത്ത സി.എസ്.എസ്. സ്റ്റൈൽ ഭാഗം കണ്ടെത്താനായി.",
"uploaded-event-handler-on-svg": "എസ്.വി.ജി. പ്രമാണങ്ങളിൽ എവന്റ്-ഹാൻഡ്ലർ ആട്രിബ്യൂട്ടുകൾ <code>$1=\"$2\"</code> എന്ന് സജ്ജീകരിച്ചിരിക്കുന്നവ അനുവദിച്ചിട്ടില്ല.",
- "uploaded-href-unsafe-target-svg": "à´\85à´ªàµ\8dâ\80\8cà´²àµ\8bà´¡àµ\8d à´\9aàµ\86à´¯àµ\8dà´¤ à´\8eà´¸àµ\8d.വി.à´\9cà´¿. à´ªàµ\8dരമാണതàµ\8dതിൽ à´¸àµ\81à´°à´\95àµ\8dഷിതമലàµ\8dലാതàµ\8dà´¤ à´²à´\95àµ\8dà´·àµ\8dയമായ <code><$1 $2=\"$3\"></code> à´\95à´£àµ\8dà´\9fàµ\86à´¤àµ\8dതി.",
+ "uploaded-href-unsafe-target-svg": "à´¸àµ\81à´°à´\95àµ\8dഷിതമലàµ\8dലാതàµ\8dà´¤ à´¡àµ\87à´±àµ\8dറയിലàµ\87à´\95àµ\8dà´\95àµ\81à´³àµ\8dà´³ à´\95à´£àµ\8dണി à´\95à´£àµ\8dà´\9fàµ\86à´¤àµ\8dതി: à´¯àµ\81.à´\86ർ.à´\90. à´²à´\95àµ\8dà´·àµ\8dയമായ <code><$1 $2=\"$3\"></code> à´\85à´ªàµ\8dâ\80\8cà´²àµ\8bà´¡àµ\8d à´\9aàµ\86à´¯àµ\8dà´¤ à´\8eà´¸àµ\8d.വി.à´\9cà´¿. à´ªàµ\8dരമാണതàµ\8dതിൽ à´\89à´£àµ\8dà´\9fàµ\8d.",
"uploaded-animate-svg": "അപ്ലോഡ് ചെയ്ത എസ്.വി.ജി. പ്രമാണത്തിൽ <code><$1 $2=\"$3\"></code> ആട്രിബ്യൂട്ട് ഉപയോഗിച്ച് href മാറ്റിയേക്കാവുന്ന \"animate\" റ്റാഗായ <code><$1 $2=\"$3\"></code> കണ്ടെത്തി.",
"uploaded-setting-event-handler-svg": "അപ്ലോഡ് ചെയ്ത എസ്.വി.ജി. പ്രമാണത്തിൽ <code><$1 $2=\"$3\"></code> കണ്ടെത്തി, ഇവന്റ്-കൈകാര്യ സജ്ജീകരണ ആട്രിബ്യൂട്ടുകൾ തടഞ്ഞിരിക്കുന്നു.",
"uploaded-setting-href-svg": "മാതൃഘടകത്തിലേക്ക് \"href\" ആട്രിബ്യൂട്ട് ചേർക്കാൻ \"set\" പതാക ഉപയോഗിക്കുന്നത് തടഞ്ഞിരിക്കുന്നു.",
"backend-fail-read": "$1 എന്ന പ്രമാണം വായിക്കാൻ കഴിഞ്ഞില്ല.",
"backend-fail-create": "$1 എന്ന പ്രമാണം സൃഷ്ടിക്കാൻ കഴിഞ്ഞില്ല.",
"backend-fail-maxsize": "{{PLURAL:$2|$2 ബൈറ്റ്സിലും|$2 ബൈറ്റിലും}} വലുതാണെന്ന കാരണത്താൽ $1 എന്ന പ്രമാണം സൃഷ്ടിക്കാൻ കഴിഞ്ഞില്ല.",
- "backend-fail-readonly": "സംഭരണ ബാക്കെൻഡ് \"$1\" ഇപ്പോൾ കാണൽ-മാത്രം (read-only) രീതിയിലാണ്. നൽകിയിരിക്കുന്ന കാരണം: \"''$2''\"",
+ "backend-fail-readonly": "സംഭരണ ബാക്കെൻഡ് \"$1\" ഇപ്പോൾ കാണൽ-മാത്രം (read-only) രീതിയിലാണ്. നൽകിയിരിക്കുന്ന കാരണം: <em>$2</em>",
"backend-fail-synced": "ആന്തരിക ശേഖരണ ബാക്കെൻഡിൽ പ്രമാണം \"$1\" അസ്ഥിരാവസ്ഥയിലാണുള്ളത്",
"backend-fail-connect": "\"$1\" ശേഖരണ ബാക്കെൻഡുമായി ബന്ധപ്പെടാൻ കഴിഞ്ഞില്ല.",
"backend-fail-internal": "\"$1\" എന്ന സ്റ്റോറേജ് ബാക്കെൻഡിൽ അപരിചിതമായ പിഴവ് സംഭവിച്ചു.",
"uploadstash-summary": "അപ്ലോഡ് ചെയ്യപ്പെട്ടതും (അല്ലെങ്കിൽ ചെയ്തുകൊണ്ടിരിക്കുന്നതും) അതേസമയം വിക്കിയിൽ പ്രസിദ്ധീകരിക്കാത്തതുമായ പ്രമാണങ്ങളിലേയ്ക്ക് എത്താനുള്ള സൗകര്യം ഈ താൾ നൽകുന്നു. ഈ പ്രമാണങ്ങൾ അപ്ലോഡ് ചെയ്ത ആൾക്കൊഴികെ മറ്റാർക്കും കാണാവുന്നതല്ല.",
"uploadstash-clear": "രഹസ്യമാക്കിയ പ്രമാണങ്ങൾ ശൂന്യമാക്കുക",
"uploadstash-nofiles": "താങ്കൾക്ക് രഹസ്യമാക്കിയ പ്രമാണങ്ങൾ ഒന്നുമില്ല.",
- "uploadstash-badtoken": "à´ªàµ\8dà´°à´µàµ\83à´¤àµ\8dതി വിà´\9cà´¯à´\95രമായിരàµ\81à´¨àµ\8dനിലàµ\8dà´², താങ്കളുടെ തിരുത്തുവാനുള്ള അവകാശങ്ങൾ ചിലപ്പോൾ കാലഹരണപ്പെട്ടിട്ടുണ്ടാകാം. വീണ്ടും ശ്രമിക്കുക.",
+ "uploadstash-badtoken": "à´ªàµ\8dà´°à´µàµ\83à´¤àµ\8dതി പരാà´\9cയപàµ\8dà´ªàµ\86à´\9fàµ\8dà´\9fàµ\81, താങ്കളുടെ തിരുത്തുവാനുള്ള അവകാശങ്ങൾ ചിലപ്പോൾ കാലഹരണപ്പെട്ടിട്ടുണ്ടാകാം. വീണ്ടും ശ്രമിക്കുക.",
"uploadstash-errclear": "പ്രമാണങ്ങൾ ശൂന്യമാക്കൽ പരാജയപ്പെട്ടു.",
"uploadstash-refresh": "പ്രമാണങ്ങളുടെ പട്ടിക പുതുക്കുക",
"uploadstash-thumbnail": "ലഘുചിത്രം കാണുക",
"apihelp-no-such-module": "ഘടകം \"$1\" കണ്ടെത്താനായില്ല.",
"apisandbox": "എ.പി.ഐ. എഴുത്തുകളരി",
"apisandbox-api-disabled": "ഈ സൈറ്റിൽ എ.പി.ഐ. പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു.",
- "apisandbox-intro": "'''മീഡിയവിക്കി വെബ് സെർവീസ് എ.പി.ഐ.'''യിൽ പരീക്ഷണങ്ങൾ നടത്താൻ ഈ താൾ ഉപയോഗിക്കുക.\nഎ.പി.ഐ.യുടെ ഉപയോഗത്തെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്കായി [https://www.mediawiki.org/wiki/API:Main_page the എ.പി.ഐ. സഹായം] പരിശോധിക്കുക. ഉദാഹരണം: [https://www.mediawiki.org/wiki/API#A_simple_example പ്രധാന താളിന്റെ ഉള്ളടക്കം എടുക്കുക]. കൂടുതൽ ഉദാഹരണങ്ങൾക്കായി പ്രവൃത്തി തിരഞ്ഞെടുക്കുക.\n\nഇതൊരു പരീക്ഷണകളരിയാണെങ്കിലും ഇവിടെ ചെയ്യുന്നവ വിക്കിയിൽ മാറ്റങ്ങൾ വരുത്തിയേക്കാമെന്ന് ഓർക്കുക.",
+ "apisandbox-intro": "<strong>മീഡിയവിക്കി വെബ് സെർവീസ് എ.പി.ഐ.</strong>യിൽ പരീക്ഷണങ്ങൾ നടത്താൻ ഈ താൾ ഉപയോഗിക്കുക.\nഎ.പി.ഐ.യുടെ ഉപയോഗത്തെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്കായി [[mw:API:Main page|എ.പി.ഐ. സഹായം]] പരിശോധിക്കുക. ഉദാഹരണം: [https://www.mediawiki.org/wiki/API#A_simple_example പ്രധാന താളിന്റെ ഉള്ളടക്കം എടുക്കുക]. കൂടുതൽ ഉദാഹരണങ്ങൾക്കായി പ്രവൃത്തി തിരഞ്ഞെടുക്കുക.\n\nഇതൊരു പരീക്ഷണകളരിയാണെങ്കിലും ഇവിടെ ചെയ്യുന്നവ വിക്കിയിൽ മാറ്റങ്ങൾ വരുത്തിയേക്കാമെന്ന് ഓർക്കുക.",
"apisandbox-unfullscreen": "താൾ പ്രദർശിപ്പിക്കുക",
"apisandbox-submit": "അഭ്യർത്ഥിക്കുക",
"apisandbox-reset": "ശൂന്യമാക്കുക",
"version-poweredby-others": "മറ്റുള്ളവർ",
"version-poweredby-translators": "പരിഭാഷാവിക്കിയിലെ പരിഭാഷകർ",
"version-credits-summary": "[[Special:Version|മീഡിയവിക്കിയ്ക്ക്]] നൽകിയ സംഭാവനകളുടെ പേരിൽ താഴെക്കൊടുക്കുന്നവർക്ക് ഞങ്ങൾ നന്ദി പറയുന്നു.",
- "version-license-info": "മീഡിയവിക്കി ഒരു സ്വതന്ത്ര സോഫ്റ്റ്വേറാണ്; സ്വതന്ത്ര സോഫ്റ്റ്വേർ ഫൗണ്ടേഷൻ പ്രസിദ്ധീകരിച്ചിട്ടുള്ള ഗ്നു സാർവ്വജനിക അനുവാദപത്രത്തിന്റെ പതിപ്പ് 2 പ്രകാരമോ, അല്ലെങ്കിൽ (താങ്കളുടെ ഇച്ഛാനുസരണം) പിന്നീട് പ്രസിദ്ധീകരിച്ച ഏതെങ്കിലും പതിപ്പ് പ്രകാരമോ താങ്കൾക്കിത് പുനർവിതരണം ചെയ്യാനും ഒപ്പം/അല്ലെങ്കിൽ മാറ്റങ്ങൾ വരുത്താനും സാധിക്കുന്നതാണ്.\n\nമീഡിയവിക്കി താങ്കൾക്കുപകരിക്കുമെന്ന പ്രതീക്ഷയോടെയാണ് വിതരണം ചെയ്യുന്നത്, പക്ഷേ യാതൊരു ഗുണമേന്മോത്തരവാദിത്തവും വഹിക്കുന്നില്ല; വ്യാപാരയോഗ്യമെന്നോ പ്രത്യേക ഉപയോഗത്തിന് അനുയോജ്യമെന്നോ ഉള്ള യാതൊരു ഗുണമേന്മോത്തരവാദിത്തവും ഇത് ഉൾക്കൊള്ളുന്നില്ല. കൂടുതൽ വിവരങ്ങൾക്ക് ഗ്നു സാർവ്വജനിക അനുവാദപത്രം കാണുക.\n\nഈ പ്രോഗ്രാമിനൊപ്പം [{{SERVER}}{{SCRIPTPATH}}/COPYING ഗ്നു സാർവ്വജനിക അനുവാദപത്രത്തിന്റെ ഒരു പകർപ്പ്] താങ്കൾക്ക് ലഭിച്ചിരിക്കും; ഇല്ലെങ്കിൽ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA എന്ന വിലാസത്തിലെഴുതുക അല്ലെങ്കിൽ [//www.gnu.org/licenses/old-licenses/gpl-2.0.html അനുവാദപത്രം ഓൺലൈനായി വായിക്കുക].",
+ "version-license-info": "മീഡിയവിക്കി ഒരു സ്വതന്ത്ര സോഫ്റ്റ്വേറാണ്; സ്വതന്ത്ര സോഫ്റ്റ്വേർ ഫൗണ്ടേഷൻ പ്രസിദ്ധീകരിച്ചിട്ടുള്ള ഗ്നു സാർവ്വജനിക അനുവാദപത്രത്തിന്റെ പതിപ്പ് 2 പ്രകാരമോ, അല്ലെങ്കിൽ (താങ്കളുടെ ഇച്ഛാനുസരണം) പിന്നീട് പ്രസിദ്ധീകരിച്ച ഏതെങ്കിലും പതിപ്പ് പ്രകാരമോ താങ്കൾക്കിത് പുനർവിതരണം ചെയ്യാനും ഒപ്പം/അല്ലെങ്കിൽ മാറ്റങ്ങൾ വരുത്താനും സാധിക്കുന്നതാണ്.\n\nമീഡിയവിക്കി താങ്കൾക്കുപകരിക്കുമെന്ന പ്രതീക്ഷയോടെയാണ് വിതരണം ചെയ്യുന്നത്, പക്ഷേ <em>യാതൊരു ഗുണമേന്മോത്തരവാദിത്തവും വഹിക്കുന്നില്ല</em>; <strong>വ്യാപാരയോഗ്യമെന്നോ പ്രത്യേക ഉപയോഗത്തിന് അനുയോജ്യമെന്നോ</strong> ഉള്ള യാതൊരു ഗുണമേന്മോത്തരവാദിത്തവും ഇത് ഉൾക്കൊള്ളുന്നില്ല. കൂടുതൽ വിവരങ്ങൾക്ക് ഗ്നു സാർവ്വജനിക അനുവാദപത്രം കാണുക.\n\nഈ പ്രോഗ്രാമിനൊപ്പം [{{SERVER}}{{SCRIPTPATH}}/COPYING ഗ്നു സാർവ്വജനിക അനുവാദപത്രത്തിന്റെ ഒരു പകർപ്പ്] താങ്കൾക്ക് ലഭിച്ചിരിക്കും; ഇല്ലെങ്കിൽ സ്വതന്ത്ര സോഫ്റ്റ്വേർ ഫൗണ്ടേഷൻ.ഇൻക്., 51 ഫ്രാങ്ക്ലിൻ തെരുവ്, അഞ്ചാം നില, ബോസ്റ്റൺ, എം.എ. 02110-1301, അമേരിക്കൻ ഐക്യനാടുകൾ എന്ന വിലാസത്തിലെഴുതുക അല്ലെങ്കിൽ [//www.gnu.org/licenses/old-licenses/gpl-2.0.html അനുവാദപത്രം ഓൺലൈനായി വായിക്കുക].",
"version-software": "ഇൻസ്റ്റോൾ ചെയ്ത സോഫ്റ്റ്വെയർ",
"version-software-product": "ഉല്പന്നം",
"version-software-version": "പതിപ്പ്",
"tags-create-reason": "കാരണം:",
"tags-create-submit": "സൃഷ്ടിക്കുക",
"tags-create-no-name": "റ്റാഗിന്റെ പേര് വ്യക്തമാക്കേണ്ടതുണ്ട്.",
- "tags-create-invalid-chars": "à´\9fà´¾à´\97à´¿à´¨àµ\8dà´±àµ\86 à´ªàµ\87രിൽ à´\85à´²àµ\8dപവിരാമà´\99àµ\8dà´\99à´³àµ\8b (<code>,</code>), à´®àµ\81à´¨àµ\8dà´¨àµ\8bà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´¸àµ\8dലാഷàµ\8b (<code>/</code>) à´\89à´£àµ\8dà´\9fായിരിà´\95àµ\8dà´\95ാൻ പാà´\9fàµ\81à´³àµ\8dളതലàµ\8dà´².",
+ "tags-create-invalid-chars": "à´\9fà´¾à´\97à´¿à´¨àµ\8dà´±àµ\86 à´ªàµ\87രിൽ à´\85à´²àµ\8dപവിരാമà´\99àµ\8dà´\99à´³àµ\8b (<code>,</code>), à´ªàµ\88à´ªàµ\8dà´ªàµ\81à´\95à´³àµ\8b (<code>|</code>), à´®àµ\81à´¨àµ\8dà´¨àµ\8bà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´¸àµ\8dലാഷàµ\8b (<code>/</code>) à´\89à´£àµ\8dà´\9fാവരàµ\81à´¤àµ\8d.",
"tags-create-invalid-title-chars": "ടാഗിന്റെ പേരിൽ താളിന്റെ തലക്കെട്ടിൽ ഉൾപ്പെടുത്താൻ പാടില്ലാത്ത അക്ഷരങ്ങളൊന്നുമുണ്ടാവാൻ പാടില്ല.",
"tags-create-already-exists": "\"$1\" എന്ന ടാഗ് നിലവിലുണ്ട്.",
"tags-create-warnings-above": "\"$1\" എന്ന ടാഗ് സൃഷ്ടിക്കാൻ ശ്രമിക്കുമ്പോൾ താഴെക്കൊടുത്തിരിക്കുന്ന {{PLURAL:$2|മുന്നറിയിപ്പ്|മുന്നറിയിപ്പുകൾ}} വന്നു:",
"tags-delete-not-allowed": "അനുബന്ധം വ്യക്തമായി അനുവദിക്കുന്നില്ലെങ്കിൽ, അനുബന്ധങ്ങൾ വഴി നിർവ്വചിക്കുന്ന ടാഗുകൾ മായ്ക്കാനാവുകയില്ല.",
"tags-delete-not-found": "\"$1\" എന്ന ടാഗ് നിലവിലില്ല.",
"tags-delete-too-many-uses": "\"$1\" എന്ന ടാഗ് {{PLURAL:$2|ഒന്നിലധികം നാൾപ്പതിപ്പുകളിൽ|$2 എണ്ണത്തിലധികം നാൾപ്പതിപ്പുകളിൽ}} ഉപയോഗിക്കുന്നു, അതിനാൽ അത് മായ്ക്കാനാവില്ല.",
- "tags-delete-warnings-after-delete": "\"$1\" à´\8eà´¨àµ\8dà´¨ à´\9fà´¾à´\97àµ\8d വിà´\9cà´¯à´\95രമായി മായàµ\8dà´\9aàµ\8dà´\9aà´¿à´°à´¿à´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨àµ\81, à´ªà´\95àµ\8dà´·àµ\87 à´\87നിà´\95àµ\8dà´\95àµ\8aà´\9fàµ\81à´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ {{PLURAL:$2|à´®àµ\81à´¨àµ\8dനറിയിപàµ\8dà´ªàµ\8d|à´®àµ\81à´¨àµ\8dനറിയിപàµ\8dà´ªàµ\81à´\95ൾ}} à´\89à´£àµ\8dà´\9fായി:",
+ "tags-delete-warnings-after-delete": "\"$1\" എന്ന ടാഗ് മായ്ച്ചിരിക്കുന്നു, പക്ഷേ ഇനിക്കൊടുക്കുന്ന {{PLURAL:$2|മുന്നറിയിപ്പ്|മുന്നറിയിപ്പുകൾ}} ഉണ്ടായി:",
"tags-activate-title": "ടാഗ് സജ്ജമാക്കുക",
"tags-activate-question": "താങ്കൾ, \"$1\" എന്ന ടാഗ് പ്രവർത്തനക്ഷമമാക്കാൻ പോവുകയാണ്.",
"tags-activate-reason": "കാരണം:",
"feedback-thanks": "Хвала! Ваша повратна информација је постављена на страницу „[$2 $1]“.",
"feedback-thanks-title": "Хвала вам!",
"feedback-useragent": "Кориснички агент:",
- "searchsuggest-search": "Ð\9fÑ\80еÑ\82Ñ\80ажи пÑ\80оÑ\98екаÑ\82 {{SITENAME}}",
+ "searchsuggest-search": "Ð\9fÑ\80еÑ\82Ñ\80ага",
"searchsuggest-containing": "садржи...",
"api-error-badtoken": "Унутрашња грешка: неисправан жетон.",
"api-error-emptypage": "Стварање нових празних страница није дозвољено.",
"wrongpasswordempty": "รหัสผ่านที่กรอกว่าง\nโปรดลองอีกครั้ง",
"passwordtooshort": "รหัสผ่านต้องมีอย่างน้อย $1 อักขระ",
"passwordtoolong": "รหัสผ่านยาวกว่า $1 อักขระไม่ได้",
- "passwordtoopopular": "à¹\83à¸\8aà¹\89รหัสà¸\9cà¹\88าà¸\99à¸\97ีà¹\88มีà¸\9cูà¹\89à¹\80ลืà¸à¸\81à¸\97ัà¹\88วà¹\84à¸\9bà¹\84มà¹\88à¹\84à¸\94à¹\89 à¸\81รุà¸\93าà¹\80ลืà¸à¸\81รหัสà¸\9cà¹\88าà¸\99à¸\97ีà¹\88มีà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¸\99à¹\89à¸à¸¢กว่านี้",
+ "passwordtoopopular": "à¹\83à¸\8aà¹\89รหัสà¸\9cà¹\88าà¸\99à¸\97ีà¹\88มีà¸\9cูà¹\89à¹\80ลืà¸à¸\81à¸\97ัà¹\88วà¹\84à¸\9bà¹\84มà¹\88à¹\84à¸\94à¹\89 à¸\81รุà¸\93าà¹\80ลืà¸à¸\81รหัสà¸\9cà¹\88าà¸\99à¸\97ีà¹\88à¸\84าà¸\94à¹\80à¸\94าà¹\84à¸\94à¹\89ยาà¸\81กว่านี้",
"password-name-match": "รหัสผ่านต้องต่างจากชื่อผู้ใช้",
"password-login-forbidden": "ห้ามใช้ชื่อผู้ใช้และรหัสผ่านนี้",
"mailmypassword": "ตั้งรหัสผ่านใหม่",
"savechanges": "บันทึกการเปลี่ยนแปลง",
"publishpage": "เผยแพร่หน้า",
"publishchanges": "เผยแพร่การเปลี่ยนแปลง",
+ "savearticle-start": "บันทึกหน้า…",
+ "savechanges-start": "บันทึกการเปลี่ยนแปลง…",
+ "publishpage-start": "เผยแพร่หน้า…",
+ "publishchanges-start": "เผยแพร่การเปลี่ยนแปลง…",
"preview": "ตัวอย่าง",
"showpreview": "แสดงตัวอย่าง",
"showdiff": "แสดงการเปลี่ยนแปลง",
// Remove unknown preferences. Special-case gadget- and userjs- as we can't
// control those names.
if ( $unknown ) {
- $this->deleteByWhere(
- $dbw,
- 'Dropping unknown preferences',
- [
- 'up_property NOT' . $dbw->buildLike( 'gadget-', $dbw->anyString() ),
- 'up_property NOT' . $dbw->buildLike( 'userjs-', $dbw->anyString() ),
- 'up_property NOT IN (' . $dbw->makeList( array_keys( $wgDefaultUserOptions ) ) . ')',
- ]
- );
+ $where = [
+ 'up_property NOT' . $dbw->buildLike( 'gadget-', $dbw->anyString() ),
+ 'up_property NOT' . $dbw->buildLike( 'userjs-', $dbw->anyString() ),
+ 'up_property NOT IN (' . $dbw->makeList( array_keys( $wgDefaultUserOptions ) ) . ')',
+ ];
+ // Allow extensions to add to the where clause to prevent deletion of their own prefs.
+ Hooks::run( 'DeleteUnknownPreferences', [ &$where, $dbw ] );
+ $this->deleteByWhere( $dbw, 'Dropping unknown preferences', $where );
}
// Something something phase 3
$mw$
BEGIN
IF TG_OP = 'INSERT' THEN
- NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
+ NEW.titlevector = to_tsvector(REPLACE(NEW.page_title,'/',' '));
ELSIF NEW.page_title != OLD.page_title THEN
- NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
+ NEW.titlevector := to_tsvector(REPLACE(NEW.page_title,'/',' '));
END IF;
RETURN NEW;
END;
CREATE INDEX job_timestamp_idx ON job (job_timestamp);
-- Tsearch2 2 stuff. Will fail if we don't have proper access to the tsearch2 tables
--- Version 8.3 or higher only. Previous versions would need another parmeter for to_tsvector.
-- Make sure you also change patch-tsearch2funcs.sql if the funcs below change.
ALTER TABLE page ADD titlevector tsvector;
CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent
FOR EACH ROW EXECUTE PROCEDURE ts2_page_text();
--- These are added by the setup script due to version compatibility issues
--- If using 8.1, we switch from "gin" to "gist"
-
CREATE INDEX ts2_page_title ON page USING gin(titlevector);
CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector);
$db->delete( $tbl, '*', __METHOD__ );
}
+ if ( $db->getType() === 'postgres' ) {
+ // Reset the table's sequence too.
+ $db->resetSequenceForTable( $tbl, __METHOD__ );
+ }
+
if ( $tbl === 'page' ) {
// Forget about the pages since they don't
// exist in the DB.
'trxProfiler' => new TransactionProfiler(),
'connLogger' => new \Psr\Log\NullLogger(),
'queryLogger' => new \Psr\Log\NullLogger(),
- 'errorLogger' => new \Psr\Log\NullLogger(),
+ 'errorLogger' => function () {
+ },
+ 'deprecationLogger' => function () {
+ },
'type' => 'test',
'dbname' => $dbName,
'tablePrefix' => $dbPrefix,
--- /dev/null
+<?php
+
+use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\DatabasePostgres;
+use Wikimedia\ScopedCallback;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @group Database
+ */
+class DatabasePostgresTest extends MediaWikiTestCase {
+
+ private function doTestInsertIgnore() {
+ $reset = new ScopedCallback( function () {
+ if ( $this->db->explicitTrxActive() ) {
+ $this->db->rollback( __METHOD__ );
+ }
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
+ } );
+
+ $this->db->query(
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)"
+ );
+ $this->db->insert( 'foo', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
+
+ // Normal INSERT IGNORE
+ $this->db->begin( __METHOD__ );
+ $this->db->insert(
+ 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__, [ 'IGNORE' ]
+ );
+ $this->assertSame( 2, $this->db->affectedRows() );
+ $this->assertSame(
+ [ '1', '2', '3', '5' ],
+ $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
+ );
+ $this->db->rollback( __METHOD__ );
+
+ // INSERT IGNORE doesn't ignore stuff like NOT NULL violations
+ $this->db->begin( __METHOD__ );
+ $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
+ try {
+ $this->db->insert(
+ 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__, [ 'IGNORE' ]
+ );
+ $this->db->endAtomic( __METHOD__ );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( DBQueryError $e ) {
+ $this->assertSame( 0, $this->db->affectedRows() );
+ $this->db->cancelAtomic( __METHOD__ );
+ }
+ $this->assertSame(
+ [ '1', '2' ],
+ $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
+ );
+ $this->db->rollback( __METHOD__ );
+ }
+
+ /**
+ * @covers Wikimedia\Rdbms\DatabasePostgres::insert
+ */
+ public function testInsertIgnoreOld() {
+ if ( !$this->db instanceof DatabasePostgres ) {
+ $this->markTestSkipped( 'Not PostgreSQL' );
+ }
+ if ( $this->db->getServerVersion() < 9.5 ) {
+ $this->doTestInsertIgnore();
+ } else {
+ // Hack version to make it take the old code path
+ $w = TestingAccessWrapper::newFromObject( $this->db );
+ $oldVer = $w->numericVersion;
+ $w->numericVersion = 9.4;
+ try {
+ $this->doTestInsertIgnore();
+ } finally {
+ $w->numericVersion = $oldVer;
+ }
+ }
+ }
+
+ /**
+ * @covers Wikimedia\Rdbms\DatabasePostgres::insert
+ */
+ public function testInsertIgnoreNew() {
+ if ( !$this->db instanceof DatabasePostgres ) {
+ $this->markTestSkipped( 'Not PostgreSQL' );
+ }
+ if ( $this->db->getServerVersion() < 9.5 ) {
+ $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() );
+ }
+
+ $this->doTestInsertIgnore();
+ }
+
+ private function doTestInsertSelectIgnore() {
+ $reset = new ScopedCallback( function () {
+ if ( $this->db->explicitTrxActive() ) {
+ $this->db->rollback( __METHOD__ );
+ }
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
+ $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ) );
+ } );
+
+ $this->db->query(
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)"
+ );
+ $this->db->query(
+ "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)"
+ );
+ $this->db->insert( 'bar', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
+
+ // Normal INSERT IGNORE
+ $this->db->begin( __METHOD__ );
+ $this->db->insert( 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__ );
+ $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] );
+ $this->assertSame( 2, $this->db->affectedRows() );
+ $this->assertSame(
+ [ '1', '2', '3', '5' ],
+ $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
+ );
+ $this->db->rollback( __METHOD__ );
+
+ // INSERT IGNORE doesn't ignore stuff like NOT NULL violations
+ $this->db->begin( __METHOD__ );
+ $this->db->insert( 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__ );
+ $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
+ try {
+ $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] );
+ $this->db->endAtomic( __METHOD__ );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( DBQueryError $e ) {
+ $this->assertSame( 0, $this->db->affectedRows() );
+ $this->db->cancelAtomic( __METHOD__ );
+ }
+ $this->assertSame(
+ [ '1', '2' ],
+ $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
+ );
+ $this->db->rollback( __METHOD__ );
+ }
+
+ /**
+ * @covers Wikimedia\Rdbms\DatabasePostgres::nativeInsertSelect
+ */
+ public function testInsertSelectIgnoreOld() {
+ if ( !$this->db instanceof DatabasePostgres ) {
+ $this->markTestSkipped( 'Not PostgreSQL' );
+ }
+ if ( $this->db->getServerVersion() < 9.5 ) {
+ $this->doTestInsertSelectIgnore();
+ } else {
+ // Hack version to make it take the old code path
+ $w = TestingAccessWrapper::newFromObject( $this->db );
+ $oldVer = $w->numericVersion;
+ $w->numericVersion = 9.4;
+ try {
+ $this->doTestInsertSelectIgnore();
+ } finally {
+ $w->numericVersion = $oldVer;
+ }
+ }
+ }
+
+ /**
+ * @covers Wikimedia\Rdbms\DatabasePostgres::nativeInsertSelect
+ */
+ public function testInsertSelectIgnoreNew() {
+ if ( !$this->db instanceof DatabasePostgres ) {
+ $this->markTestSkipped( 'Not PostgreSQL' );
+ }
+ if ( $this->db->getServerVersion() < 9.5 ) {
+ $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() );
+ }
+
+ $this->doTestInsertSelectIgnore();
+ }
+
+}
/** @var array List of row arrays */
protected $nextResult = [];
+ /** @var array|null */
+ protected $nextError = null;
+ /** @var array|null */
+ protected $lastError = null;
+
/**
* Array of tables to be considered as existing by tableExist()
* Use setExistingTables() to alter.
$this->nextResult = $res;
}
+ /**
+ * @param int $errno Error number
+ * @param string $error Error text
+ * @param array $options
+ * - wasKnownStatementRollbackError: Return value for wasKnownStatementRollbackError()
+ */
+ public function forceNextQueryError( $errno, $error, $options = [] ) {
+ $this->nextError = [ 'errno' => $errno, 'error' => $error ] + $options;
+ }
+
protected function addSql( $sql ) {
// clean up spaces before and after some words and the whole string
$this->lastSqls[] = trim( preg_replace(
return; // no $fname parameter
}
- if ( substr( $fname, 0, strlen( $this->testName ) ) !== $this->testName ) {
+ // Handle some internal calls from the Database class
+ $check = $fname;
+ if ( preg_match( '/^Wikimedia\\\\Rdbms\\\\Database::query \((.+)\)$/', $fname, $m ) ) {
+ $check = $m[1];
+ }
+
+ if ( substr( $check, 0, strlen( $this->testName ) ) !== $this->testName ) {
throw new MWException( 'function name does not start with test class. ' .
$fname . ' vs. ' . $this->testName . '. ' .
'Please provide __METHOD__ to database methods.' );
public function query( $sql, $fname = '', $tempIgnore = false ) {
$this->checkFunctionName( $fname );
- $this->addSql( $sql );
return parent::query( $sql, $fname, $tempIgnore );
}
}
function lastErrno() {
- return -1;
+ return $this->lastError ? $this->lastError['errno'] : -1;
}
function lastError() {
- return 'test';
+ return $this->lastError ? $this->lastError['error'] : 'test';
+ }
+
+ protected function wasKnownStatementRollbackError() {
+ return isset( $this->lastError['wasKnownStatementRollbackError'] )
+ ? $this->lastError['wasKnownStatementRollbackError']
+ : false;
}
function fieldInfo( $table, $field ) {
}
protected function doQuery( $sql ) {
+ $sql = preg_replace( '< /\* .+? \*/>', '', $sql );
+ $this->addSql( $sql );
+
+ if ( $this->nextError ) {
+ $this->lastError = $this->nextError;
+ $this->nextError = null;
+ return false;
+ }
+
$res = $this->nextResult;
$this->nextResult = [];
+ $this->lastError = null;
return new FakeResultWrapper( $res );
}
} else {
$sender->expects( $this->never() )->method( 'write' );
}
- mt_srand( $seed );
+ if ( defined( 'MT_RAND_PHP' ) ) {
+ mt_srand( $seed, MT_RAND_PHP );
+ } else {
+ mt_srand( $seed );
+ }
$client = new SamplingStatsdClient( $sender );
$client->send( $data, $sampleRate );
}
use Wikimedia\Rdbms\LikeMatch;
use Wikimedia\Rdbms\Database;
use Wikimedia\TestingAccessWrapper;
+use Wikimedia\Rdbms\DBTransactionStateError;
use Wikimedia\Rdbms\DBUnexpectedError;
/**
$this->assertEquals( 0, $this->database->trxLevel() );
}
+ /**
+ * @covers \Wikimedia\Rdbms\Database::query
+ */
+ public function testImplicitTransactionRollback() {
+ $doError = function ( $wasKnown = true ) {
+ $this->database->forceNextQueryError( 666, 'Evilness' );
+ try {
+ $this->database->delete( 'error', '1', __CLASS__ . '::SomeCaller' );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( DBError $e ) {
+ $this->assertSame( 666, $e->errno );
+ }
+ };
+
+ $this->database->setFlag( Database::DBO_TRX );
+
+ // Implicit transaction gets silently rolled back
+ $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
+ call_user_func( $doError, false );
+ $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+ $this->database->commit( __METHOD__, Database::FLUSHING_INTERNAL );
+ // phpcs:ignore
+ $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; ROLLBACK; BEGIN; DELETE FROM x WHERE field = \'1\'; COMMIT' );
+
+ // ... unless there were prior writes
+ $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
+ $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+ call_user_func( $doError, false );
+ try {
+ $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( DBTransactionStateError $e ) {
+ }
+ $this->database->rollback( __METHOD__, Database::FLUSHING_INTERNAL );
+ // phpcs:ignore
+ $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'1\'; DELETE FROM error WHERE 1; ROLLBACK' );
+ }
+
/**
* @covers \Wikimedia\Rdbms\Database::close
*/